mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-01 02:41:48 +00:00
Compare commits
142 Commits
v2025.1.1-
...
v2025.1.1-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a3ccf93c6 | ||
|
|
d92f17b014 | ||
|
|
4c225ef2c1 | ||
|
|
561078ce29 | ||
|
|
d312bccfeb | ||
|
|
9826539198 | ||
|
|
33f7067216 | ||
|
|
ac1836ec44 | ||
|
|
57e10755fd | ||
|
|
8ec22b7d5c | ||
|
|
a04c40f589 | ||
|
|
b4bec566f0 | ||
|
|
d76827db48 | ||
|
|
602c4caa02 | ||
|
|
661c321fe2 | ||
|
|
d1de7663d3 | ||
|
|
b040059108 | ||
|
|
0798ac53d0 | ||
|
|
ded7c87d63 | ||
|
|
2acf111f56 | ||
|
|
aa7dd258c4 | ||
|
|
ca51197486 | ||
|
|
91142ba5fe | ||
|
|
969664ceaa | ||
|
|
1e545c38a8 | ||
|
|
6eb652e10e | ||
|
|
fff73ee6e1 | ||
|
|
bade0a8716 | ||
|
|
453335e354 | ||
|
|
c289562a06 | ||
|
|
07345712dc | ||
|
|
425bf83036 | ||
|
|
6adad7bad7 | ||
|
|
280d2c7e32 | ||
|
|
edc3963955 | ||
|
|
c58be2580d | ||
|
|
f40bd3593d | ||
|
|
3cc541f261 | ||
|
|
811b130968 | ||
|
|
d39dfd64ea | ||
|
|
661bae568f | ||
|
|
01f85abcfe | ||
|
|
396f8203ac | ||
|
|
4a43ddbacf | ||
|
|
0921054a28 | ||
|
|
f738fc92f0 | ||
|
|
a0f38f83f9 | ||
|
|
876be30724 | ||
|
|
de318fab91 | ||
|
|
8b8b634f65 | ||
|
|
fd2e0c0427 | ||
|
|
a66fa339dc | ||
|
|
44a45d44e2 | ||
|
|
af652817d9 | ||
|
|
5a16b0e108 | ||
|
|
71c050389a | ||
|
|
83fa422338 | ||
|
|
3113627be6 | ||
|
|
f2d2500d1d | ||
|
|
63e623d70b | ||
|
|
9e8d37c03b | ||
|
|
ad09d73dd6 | ||
|
|
3fd33b1f72 | ||
|
|
043c155087 | ||
|
|
7a6c7af412 | ||
|
|
8588b5e520 | ||
|
|
debb52156c | ||
|
|
23e71e10e4 | ||
|
|
44c0bbc4a9 | ||
|
|
a48f3c35f4 | ||
|
|
7c91b81906 | ||
|
|
eb8583596c | ||
|
|
dfd1084526 | ||
|
|
2cfe114c78 | ||
|
|
caae5357b7 | ||
|
|
22e91bfacd | ||
|
|
67e1b5fe95 | ||
|
|
27e07d6787 | ||
|
|
e49d452e46 | ||
|
|
5c0edc2410 | ||
|
|
309b370223 | ||
|
|
f620141e0d | ||
|
|
5f3cf517d3 | ||
|
|
328a781040 | ||
|
|
ebf83e4340 | ||
|
|
9f6f267f5c | ||
|
|
21980c7447 | ||
|
|
75fc4d18ef | ||
|
|
8f81b7723d | ||
|
|
fe45265a3a | ||
|
|
0f313c672f | ||
|
|
aaf139320e | ||
|
|
89c5d98fe9 | ||
|
|
defcc02806 | ||
|
|
e6e928d670 | ||
|
|
f03e0cdf6a | ||
|
|
412c042c6c | ||
|
|
f44c3eda43 | ||
|
|
018dcaea4f | ||
|
|
85ffb7814b | ||
|
|
6207992709 | ||
|
|
42a433b6fa | ||
|
|
2c857cd82a | ||
|
|
1c220ebc60 | ||
|
|
1cfed736ce | ||
|
|
46b5631ba7 | ||
|
|
d2b19d8928 | ||
|
|
9a5f73d787 | ||
|
|
db552317e7 | ||
|
|
03cb3c70b4 | ||
|
|
ac907f755a | ||
|
|
a3b12b3bd9 | ||
|
|
cbc9264468 | ||
|
|
28ac2e3554 | ||
|
|
58c0cd46b1 | ||
|
|
115a02211c | ||
|
|
f553dee6cb | ||
|
|
e8d2d1c39a | ||
|
|
7cc7fa1845 | ||
|
|
cbdb4e81f6 | ||
|
|
05c7fd929b | ||
|
|
0c824bd447 | ||
|
|
ed18b41198 | ||
|
|
6745fc7c2f | ||
|
|
40af8db28a | ||
|
|
5ac132f6a2 | ||
|
|
dd72a78aa4 | ||
|
|
36e0c9d6db | ||
|
|
95b9bd880b | ||
|
|
2054d0f57e | ||
|
|
ee22482f4a | ||
|
|
796dbd3b86 | ||
|
|
0424e5ba36 | ||
|
|
f7dddb8014 | ||
|
|
68715aa484 | ||
|
|
fad06ae1e7 | ||
|
|
40caabea23 | ||
|
|
59dc9ad8f4 | ||
|
|
2b1c5aa4fc | ||
|
|
0bada2e102 | ||
|
|
ee281ea448 | ||
|
|
bedfc09268 |
20
.bazelignore
Normal file
20
.bazelignore
Normal file
@@ -0,0 +1,20 @@
|
||||
build_cmake
|
||||
build-cmake
|
||||
|
||||
# Auto generated by vscode
|
||||
apriltag/bin
|
||||
cameraserver/bin
|
||||
cameraserver/multiCameraServer/bin
|
||||
cscore/bin
|
||||
fieldImages/bin
|
||||
hal/bin
|
||||
developerRobot/bin
|
||||
ntcore/bin
|
||||
romiVendordep/bin
|
||||
wpilibNewCommands/bin
|
||||
wpilibj/bin
|
||||
wpimath/bin
|
||||
wpinet/bin
|
||||
wpiutil/bin
|
||||
wpiunits/bin
|
||||
xrpVendordep/bin
|
||||
51
.bazelrc
Normal file
51
.bazelrc
Normal file
@@ -0,0 +1,51 @@
|
||||
try-import %workspace%/bazel_auth.rc
|
||||
try-import %workspace%/user.bazelrc
|
||||
|
||||
common --noenable_bzlmod
|
||||
|
||||
build --java_language_version=17
|
||||
build --java_runtime_version=roboriojdk_17
|
||||
build --tool_java_language_version=17
|
||||
build --tool_java_runtime_version=remotejdk_17
|
||||
|
||||
test --test_output=errors
|
||||
test --test_verbose_timeout_warnings
|
||||
|
||||
import shared/bazel/compiler_flags/sanitizers.rc
|
||||
import shared/bazel/compiler_flags/base_linux_flags.rc
|
||||
import shared/bazel/compiler_flags/linux_flags.rc
|
||||
import shared/bazel/compiler_flags/osx_flags.rc
|
||||
import shared/bazel/compiler_flags/roborio_flags.rc
|
||||
import shared/bazel/compiler_flags/windows_flags.rc
|
||||
import shared/bazel/compiler_flags/coverage_flags.rc
|
||||
|
||||
build:build_java --test_tag_filters=allwpilib-build-java --build_tag_filters=allwpilib-build-java
|
||||
build:build_cpp --test_tag_filters=+allwpilib-build-cpp --build_tag_filters=+allwpilib-build-cpp
|
||||
build:no_example --test_tag_filters=-wpi-example --build_tag_filters=-wpi-example
|
||||
test:no_example --test_tag_filters=-wpi-example --build_tag_filters=-wpi-example
|
||||
|
||||
# Build Buddy Cache Setup
|
||||
build:build_buddy --bes_results_url=https://app.buildbuddy.io/invocation/
|
||||
build:build_buddy --bes_backend=grpcs://remote.buildbuddy.io
|
||||
build:build_buddy --remote_cache=grpcs://remote.buildbuddy.io
|
||||
build:build_buddy --remote_timeout=3600
|
||||
|
||||
# Additional suggestions from buildbuddy for speed
|
||||
build:build_buddy --experimental_remote_cache_compression
|
||||
build:build_buddy --experimental_remote_cache_compression_threshold=100
|
||||
build:build_buddy --noslim_profile
|
||||
build:build_buddy --experimental_profile_include_target_label
|
||||
build:build_buddy --experimental_profile_include_primary_output
|
||||
build:build_buddy --nolegacy_important_outputs
|
||||
|
||||
common:build_buddy_readonly --noremote_upload_local_results
|
||||
|
||||
# This config should be used locally. It downloads more than the CI version
|
||||
build:remote_user --config=build_buddy
|
||||
build:remote_user --config=build_buddy_readonly
|
||||
build:remote_user --remote_download_toplevel
|
||||
|
||||
build:ci --config=build_buddy
|
||||
build:ci --remote_download_minimal
|
||||
|
||||
build --build_metadata=REPO_URL=https://github.com/wpilibsuite/allwpilib.git
|
||||
1
.bazelversion
Normal file
1
.bazelversion
Normal file
@@ -0,0 +1 @@
|
||||
7.3.1
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -2,7 +2,7 @@
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
labels: 'type: bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -2,7 +2,7 @@
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
labels: 'type: feature'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/question.md
vendored
2
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -2,7 +2,7 @@
|
||||
name: Question
|
||||
about: Ask about features or parts of this project
|
||||
title: ''
|
||||
labels: ''
|
||||
labels: 'type: support'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
27
.github/actions/setup-build-buddy/action.yml
vendored
Normal file
27
.github/actions/setup-build-buddy/action.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: 'Setup BuildBuddy acache'
|
||||
description: 'Sets up the build buddy cache to be readonly / writing based on the presence of environment variables'
|
||||
|
||||
inputs:
|
||||
token:
|
||||
description: 'Build Buddy API token'
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Setup without key
|
||||
env:
|
||||
API_KEY: ${{ inputs.token }}
|
||||
if: ${{ env.API_KEY == '' }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo "No API key secret detected, will setup readonly cache"
|
||||
echo "build:ci --config=build_buddy_readonly" > bazel_auth.rc
|
||||
|
||||
- name: Set with key
|
||||
env:
|
||||
API_KEY: ${{ inputs.token }}
|
||||
if: ${{ env.API_KEY != '' }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo "API Key detected!"
|
||||
echo "build:build_buddy --remote_header=x-buildbuddy-api-key=${{ env.API_KEY }}" > bazel_auth.rc
|
||||
109
.github/workflows/bazel.yml
vendored
Normal file
109
.github/workflows/bazel.yml
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
name: Bazel
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { name: "Windows (native)", os: windows-2022, action: "test", config: "--config=windows", }
|
||||
- { name: "Windows (arm)", os: windows-2022, action: "build", config: "--config=windows_arm", }
|
||||
|
||||
name: "Build ${{ matrix.name }}"
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with: { fetch-depth: 0 }
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 17
|
||||
architecture: x64
|
||||
|
||||
- id: Setup_build_buddy
|
||||
uses: ./.github/actions/setup-build-buddy
|
||||
with:
|
||||
token: ${{ secrets.BUILDBUDDY_API_KEY }}
|
||||
|
||||
- name: Build Release
|
||||
run: bazel --output_user_root=C:\\bazelroot ${{ matrix.action }} -k ... --config=ci -c opt ${{ matrix.config }} --verbose_failures
|
||||
shell: bash
|
||||
|
||||
build-mac:
|
||||
name: "Mac"
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with: { fetch-depth: 0 }
|
||||
|
||||
- id: Setup_build_buddy
|
||||
uses: ./.github/actions/setup-build-buddy
|
||||
with:
|
||||
token: ${{ secrets.BUILDBUDDY_API_KEY }}
|
||||
|
||||
- name: Build Release
|
||||
run: bazel test -k ... --config=ci -c opt --config=macos --nojava_header_compilation --verbose_failures
|
||||
shell: bash
|
||||
|
||||
build-linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { name: "Linux (native)", os: ubuntu-22.04, action: "test", config: "--config=linux", }
|
||||
- { name: "Linux (roborio)", os: ubuntu-22.04, action: "build", config: "--config=roborio", }
|
||||
name: "${{ matrix.name }}"
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with: { fetch-depth: 0 }
|
||||
- uses: bazelbuild/setup-bazelisk@v3
|
||||
|
||||
- id: Setup_build_buddy
|
||||
uses: ./.github/actions/setup-build-buddy
|
||||
with:
|
||||
token: ${{ secrets.BUILDBUDDY_API_KEY }}
|
||||
|
||||
- name: Build and Test Release
|
||||
run: bazel ${{ matrix.action }} ... --config=ci -c opt ${{ matrix.config }} -k --verbose_failures
|
||||
|
||||
buildifier:
|
||||
name: "buildifier"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Set up Go 1.15.x
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
cache: false
|
||||
go-version: 1.15.x
|
||||
id: go
|
||||
|
||||
- name: Install Buildifier
|
||||
run: |
|
||||
cd $(mktemp -d)
|
||||
GO111MODULE=on go get github.com/bazelbuild/buildtools/buildifier@6.0.0
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with: { fetch-depth: 0 }
|
||||
|
||||
- name: Run buildifier
|
||||
run: buildifier -warnings all --lint=fix -r .
|
||||
|
||||
- name: Check Output
|
||||
run: git --no-pager diff --exit-code HEAD
|
||||
|
||||
- name: Generate diff
|
||||
run: git diff HEAD > bazel-lint-fixes.patch
|
||||
if: ${{ failure() }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.platform }}-bazel-lint-fixes
|
||||
path: bazel-lint-fixes.patch
|
||||
if: ${{ failure() }}
|
||||
51
.github/workflows/cmake-android.yml
vendored
Normal file
51
.github/workflows/cmake-android.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: CMake Android
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
SCCACHE_WEBDAV_ENDPOINT: "https://frcmaven.wpi.edu/artifactory/wpilib-generic-cache-cmake-local"
|
||||
SCCACHE_WEBDAV_KEY_PREFIX: "sccache"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
name: Android Arm64
|
||||
abi: arm64-v8a
|
||||
- os: ubuntu-22.04
|
||||
name: Android X64
|
||||
abi: "x86_64"
|
||||
|
||||
name: "Build - ${{ matrix.name }}"
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: nttld/setup-ndk@v1
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r27c
|
||||
add-to-path: false
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
|
||||
- name: Install sccache
|
||||
uses: mozilla-actions/sccache-action@v0.0.5
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y ninja-build
|
||||
|
||||
- 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
|
||||
|
||||
- name: build
|
||||
run: cmake --build build-cmake --parallel $(nproc)
|
||||
10
.github/workflows/comment-command.yml
vendored
10
.github/workflows/comment-command.yml
vendored
@@ -33,17 +33,17 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}"
|
||||
NUMBER: ${{ github.event.issue.number }}
|
||||
- name: Set up Python 3.10
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.12'
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Install wpiformat
|
||||
run: pip3 install wpiformat==2024.42
|
||||
run: pip3 install wpiformat==2024.45
|
||||
- name: Run wpiformat
|
||||
run: wpiformat
|
||||
- name: Run spotlessApply
|
||||
@@ -81,10 +81,10 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}"
|
||||
NUMBER: ${{ github.event.issue.number }}
|
||||
- name: Set up Python 3.9
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.9
|
||||
python-version: '3.12'
|
||||
- name: Install jinja
|
||||
run: python -m pip install jinja2
|
||||
- name: Install protobuf dependencies
|
||||
|
||||
2
.github/workflows/gradle.yml
vendored
2
.github/workflows/gradle.yml
vendored
@@ -121,7 +121,7 @@ jobs:
|
||||
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
|
||||
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
|
||||
- name: Set Keychain Lock Timeout
|
||||
run: security set-keychain-settings -lut 3600
|
||||
run: security set-keychain-settings -lut 21600
|
||||
if: |
|
||||
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
|
||||
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
|
||||
|
||||
12
.github/workflows/lint-format.yml
vendored
12
.github/workflows/lint-format.yml
vendored
@@ -22,12 +22,12 @@ jobs:
|
||||
run: |
|
||||
git checkout -b pr
|
||||
git branch -f main origin/main
|
||||
- name: Set up Python 3.10
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.12'
|
||||
- name: Install wpiformat
|
||||
run: pip3 install wpiformat==2024.42
|
||||
run: pip3 install wpiformat==2024.45
|
||||
- name: Run
|
||||
run: wpiformat
|
||||
- name: Check output
|
||||
@@ -61,12 +61,12 @@ jobs:
|
||||
git config --global --add safe.directory /__w/allwpilib/allwpilib
|
||||
git checkout -b pr
|
||||
git branch -f main origin/main
|
||||
- name: Set up Python 3.10
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.12'
|
||||
- name: Install wpiformat
|
||||
run: pip3 install wpiformat==2024.42
|
||||
run: pip3 install wpiformat==2024.45
|
||||
- name: Create compile_commands.json
|
||||
run: |
|
||||
./gradlew generateCompileCommands -Ptoolchain-optional-roboRio
|
||||
|
||||
70
.github/workflows/pregen_all.py
vendored
70
.github/workflows/pregen_all.py
vendored
@@ -6,7 +6,7 @@ import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main(argv):
|
||||
def main():
|
||||
script_path = Path(__file__).resolve()
|
||||
REPO_ROOT = script_path.parent.parent.parent
|
||||
parser = argparse.ArgumentParser()
|
||||
@@ -15,26 +15,64 @@ def main(argv):
|
||||
help="Path to the quickbuf protoc plugin",
|
||||
required=True,
|
||||
)
|
||||
args = parser.parse_args(argv)
|
||||
subprocess.run(["python", f"{REPO_ROOT}/hal/generate_usage_reporting.py"])
|
||||
subprocess.run(["python", f"{REPO_ROOT}/ntcore/generate_topics.py"])
|
||||
subprocess.run(["python", f"{REPO_ROOT}/wpimath/generate_numbers.py"])
|
||||
args = parser.parse_args()
|
||||
subprocess.run(
|
||||
[sys.executable, f"{REPO_ROOT}/hal/generate_usage_reporting.py"], check=True
|
||||
)
|
||||
subprocess.run(
|
||||
[sys.executable, f"{REPO_ROOT}/ntcore/generate_topics.py"], check=True
|
||||
)
|
||||
subprocess.run(
|
||||
[sys.executable, f"{REPO_ROOT}/wpimath/generate_numbers.py"], check=True
|
||||
)
|
||||
subprocess.run(
|
||||
[
|
||||
"python",
|
||||
sys.executable,
|
||||
f"{REPO_ROOT}/wpimath/generate_quickbuf.py",
|
||||
f"--quickbuf_plugin={args.quickbuf_plugin}",
|
||||
]
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(["python", f"{REPO_ROOT}/wpiunits/generate_units.py"])
|
||||
subprocess.run(["python", f"{REPO_ROOT}/wpilibc/generate_hids.py"])
|
||||
subprocess.run(["python", f"{REPO_ROOT}/wpilibj/generate_hids.py"])
|
||||
subprocess.run(["python", f"{REPO_ROOT}/wpilibNewCommands/generate_hids.py"])
|
||||
subprocess.run(["python", f"{REPO_ROOT}/wpilibc/generate_pwm_motor_controllers.py"])
|
||||
subprocess.run(["python", f"{REPO_ROOT}/wpilibj/generate_pwm_motor_controllers.py"])
|
||||
subprocess.run(["python", f"{REPO_ROOT}/thirdparty/imgui_suite/generate_gl3w.py"])
|
||||
subprocess.run(f"{REPO_ROOT}/thirdparty/imgui_suite/generate_fonts.sh")
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
f"{REPO_ROOT}/wpimath/generate_nanopb.py",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
[sys.executable, f"{REPO_ROOT}/wpiunits/generate_units.py"], check=True
|
||||
)
|
||||
subprocess.run(
|
||||
[sys.executable, f"{REPO_ROOT}/wpilibc/generate_hids.py"], check=True
|
||||
)
|
||||
subprocess.run(
|
||||
[sys.executable, f"{REPO_ROOT}/wpilibj/generate_hids.py"], check=True
|
||||
)
|
||||
subprocess.run(
|
||||
[sys.executable, f"{REPO_ROOT}/wpilibNewCommands/generate_hids.py"], check=True
|
||||
)
|
||||
subprocess.run(
|
||||
[sys.executable, f"{REPO_ROOT}/wpilibc/generate_pwm_motor_controllers.py"],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
[sys.executable, f"{REPO_ROOT}/wpilibj/generate_pwm_motor_controllers.py"],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
f"{REPO_ROOT}/wpiutil/generate_nanopb.py",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
[sys.executable, f"{REPO_ROOT}/thirdparty/imgui_suite/generate_gl3w.py"],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(f"{REPO_ROOT}/thirdparty/imgui_suite/generate_fonts.sh", check=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
||||
main()
|
||||
|
||||
8
.github/workflows/pregenerate.yml
vendored
8
.github/workflows/pregenerate.yml
vendored
@@ -18,12 +18,12 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python 3.9
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Install jinja
|
||||
run: python -m pip install jinja2
|
||||
python-version: '3.12'
|
||||
- name: Install jinja and protobuf
|
||||
run: python -m pip install jinja2 protobuf grpcio-tools
|
||||
- name: Install protobuf dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler && wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf-1.3.3-linux-x86_64.exe && chmod +x protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
|
||||
- name: Regenerate all
|
||||
|
||||
2
.github/workflows/sentinel-build.yml
vendored
2
.github/workflows/sentinel-build.yml
vendored
@@ -119,7 +119,7 @@ jobs:
|
||||
if: |
|
||||
matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' && github.ref == 'refs/heads/main')
|
||||
- name: Set Keychain Lock Timeout
|
||||
run: security set-keychain-settings -lut 3600
|
||||
run: security set-keychain-settings -lut 21600
|
||||
if: |
|
||||
matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' && github.ref == 'refs/heads/main')
|
||||
- name: Set Java Heap Size
|
||||
|
||||
26
.github/workflows/upstream-utils.yml
vendored
26
.github/workflows/upstream-utils.yml
vendored
@@ -22,10 +22,10 @@ jobs:
|
||||
run: |
|
||||
git checkout -b pr
|
||||
git branch -f main origin/main
|
||||
- name: Set up Python 3.9
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.9
|
||||
python-version: '3.12'
|
||||
- name: Configure committer identity
|
||||
run: |
|
||||
git config --global user.email "you@example.com"
|
||||
@@ -35,102 +35,122 @@ jobs:
|
||||
cd upstream_utils
|
||||
./apriltag.py clone
|
||||
./apriltag.py copy-src
|
||||
./apriltag.py format-patch
|
||||
- name: Run argparse_lib.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./argparse_lib.py clone
|
||||
./argparse_lib.py copy-src
|
||||
./argparse_lib.py format-patch
|
||||
- name: Run eigen.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./eigen.py clone
|
||||
./eigen.py copy-src
|
||||
./eigen.py format-patch
|
||||
- name: Run expected.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./expected.py clone
|
||||
./expected.py copy-src
|
||||
./expected.py format-patch
|
||||
- name: Run fmt.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./fmt.py clone
|
||||
./fmt.py copy-src
|
||||
./fmt.py format-patch
|
||||
- name: Run gcem.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./gcem.py clone
|
||||
./gcem.py copy-src
|
||||
./gcem.py format-patch
|
||||
- name: Run gl3w.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./gl3w.py clone
|
||||
./gl3w.py copy-src
|
||||
./gl3w.py format-patch
|
||||
- name: Run glfw.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./glfw.py clone
|
||||
./glfw.py copy-src
|
||||
./glfw.py format-patch
|
||||
- name: Run googletest.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./googletest.py clone
|
||||
./googletest.py copy-src
|
||||
./googletest.py format-patch
|
||||
- name: Run imgui.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./imgui.py clone
|
||||
./imgui.py copy-src
|
||||
./imgui.py format-patch
|
||||
- name: Run implot.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./implot.py clone
|
||||
./implot.py copy-src
|
||||
./implot.py format-patch
|
||||
- name: Run json.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./json.py clone
|
||||
./json.py copy-src
|
||||
./json.py format-patch
|
||||
- name: Run libuv.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./libuv.py clone
|
||||
./libuv.py copy-src
|
||||
./libuv.py format-patch
|
||||
- name: Run llvm.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./llvm.py clone
|
||||
./llvm.py copy-src
|
||||
./llvm.py format-patch
|
||||
- name: Run mpack.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./mpack.py clone
|
||||
./mpack.py copy-src
|
||||
./mpack.py format-patch
|
||||
- name: Run stack_walker.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./stack_walker.py clone
|
||||
./stack_walker.py copy-src
|
||||
./stack_walker.py format-patch
|
||||
- name: Run memory.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./memory.py clone
|
||||
./memory.py copy-src
|
||||
./memory.py format-patch
|
||||
- name: Run protobuf.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./protobuf.py clone
|
||||
./protobuf.py copy-src
|
||||
./protobuf.py format-patch
|
||||
- name: Run sleipnir.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./sleipnir.py clone
|
||||
./sleipnir.py copy-src
|
||||
./sleipnir.py format-patch
|
||||
- name: Run stb.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./stb.py clone
|
||||
./stb.py copy-src
|
||||
./stb.py format-patch
|
||||
- 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
|
||||
run: git --no-pager diff --exit-code HEAD ':!*.bazel'
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -15,6 +15,8 @@ networktables.json
|
||||
ntcore/connectionlistenertest.json
|
||||
ntcore/timesynctest.json
|
||||
|
||||
nanopb_pb2.py
|
||||
|
||||
# Created by the jenkins test script
|
||||
test-reports
|
||||
|
||||
@@ -249,9 +251,7 @@ imgui.ini
|
||||
/bazel-*
|
||||
user.bazelrc
|
||||
coverage_report/
|
||||
bazel_auth.rc
|
||||
|
||||
# ctest
|
||||
/Testing/
|
||||
|
||||
# protobuf
|
||||
!wpiprotoplugin.jar
|
||||
|
||||
@@ -66,6 +66,14 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
option(BUILD_SHARED_LIBS "Build with shared libs (needed for JNI)" ON)
|
||||
option(WITH_JAVA "Include Java and JNI in the build" OFF)
|
||||
option(WITH_JAVA_SOURCE "Build Java source jars" ${WITH_JAVA})
|
||||
option(WITH_DOCS "Build Doxygen docs (needs Git for versioning)" OFF)
|
||||
cmake_dependent_option(
|
||||
DOCS_WARNINGS_AS_ERRORS
|
||||
"Make docs warnings into errors"
|
||||
OFF
|
||||
WITH_DOCS
|
||||
OFF
|
||||
)
|
||||
option(WITH_CSCORE "Build cscore (needs OpenCV)" ON)
|
||||
option(WITH_NTCORE "Build ntcore" ON)
|
||||
option(WITH_WPIMATH "Build wpimath" ON)
|
||||
@@ -123,12 +131,17 @@ set(java_lib_dest java)
|
||||
if(WITH_JAVA OR WITH_JAVA_SOURCE)
|
||||
set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
|
||||
find_package(Java REQUIRED COMPONENTS Development)
|
||||
find_package(JNI REQUIRED COMPONENTS JVM)
|
||||
else()
|
||||
# Protoc requires the java runtime
|
||||
find_package(Java REQUIRED COMPONENTS Runtime)
|
||||
if(NOT ANDROID)
|
||||
find_package(JNI REQUIRED COMPONENTS JVM)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_DOCS)
|
||||
find_package(Doxygen REQUIRED)
|
||||
find_package(Git REQUIRED)
|
||||
include(AddDoxygenDocs)
|
||||
add_doxygen_docs()
|
||||
endif()
|
||||
find_package(LIBSSH CONFIG 0.7.1)
|
||||
|
||||
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
|
||||
@@ -279,8 +292,6 @@ if(WITH_NTCORE)
|
||||
add_subdirectory(ntcore)
|
||||
endif()
|
||||
|
||||
add_subdirectory(protoplugin)
|
||||
|
||||
if(WITH_WPIMATH)
|
||||
if(WITH_JAVA)
|
||||
set(WPIUNITS_DEP_REPLACE ${WPIUNITS_DEP_REPLACE_IMPL})
|
||||
|
||||
@@ -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 2024 GradleRIO version, ie `2024.0.0-alpha-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 2025 GradleRIO version, ie `2025.1.1-beta-1`
|
||||
|
||||
```groovy
|
||||
wpi.maven.useLocal = false
|
||||
@@ -28,13 +28,13 @@ Java
|
||||
```groovy
|
||||
plugins {
|
||||
id "java"
|
||||
id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
|
||||
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
|
||||
}
|
||||
|
||||
wpi.maven.useLocal = false
|
||||
wpi.maven.useDevelopment = true
|
||||
wpi.versions.wpilibVersion = '2024.+'
|
||||
wpi.versions.wpimathVersion = '2024.+'
|
||||
wpi.versions.wpilibVersion = '2025.+'
|
||||
wpi.versions.wpimathVersion = '2025.+'
|
||||
```
|
||||
|
||||
C++
|
||||
@@ -42,13 +42,13 @@ C++
|
||||
plugins {
|
||||
id "cpp"
|
||||
id "google-test-test-suite"
|
||||
id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
|
||||
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
|
||||
}
|
||||
|
||||
wpi.maven.useLocal = false
|
||||
wpi.maven.useDevelopment = true
|
||||
wpi.versions.wpilibVersion = '2024.+'
|
||||
wpi.versions.wpimathVersion = '2024.+'
|
||||
wpi.versions.wpilibVersion = '2025.+'
|
||||
wpi.versions.wpimathVersion = '2025.+'
|
||||
```
|
||||
|
||||
### Development Build Documentation
|
||||
@@ -64,7 +64,7 @@ Java
|
||||
```groovy
|
||||
plugins {
|
||||
id "java"
|
||||
id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
|
||||
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
|
||||
}
|
||||
|
||||
wpi.maven.useLocal = false
|
||||
@@ -78,7 +78,7 @@ C++
|
||||
plugins {
|
||||
id "cpp"
|
||||
id "google-test-test-suite"
|
||||
id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
|
||||
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
|
||||
}
|
||||
|
||||
wpi.maven.useLocal = false
|
||||
|
||||
25
README-Bazel.md
Normal file
25
README-Bazel.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# WPILib Bazel Support
|
||||
|
||||
WPILib is normally built with Gradle, but [Bazel](https://www.bazel.build/) can also be used to increase development speed due to the superior caching ability and the ability to use remote caching and remote execution (on select platforms)
|
||||
|
||||
|
||||
## Prerequisites
|
||||
- Install [Bazelisk](https://github.com/bazelbuild/bazelisk/releases) and add it to your path. Bazelisk is a wrapper that will download the correct version of bazel specified in the repository. Note: You can alias/rename the binary to `bazel` if you want to keep the familiar `bazel build` vs `bazelisk build` syntax.
|
||||
|
||||
## Building
|
||||
To build the entire repository, simply run `bazel build //...`. To run all of the unit tests, run `bazel test //...`
|
||||
Other examples:
|
||||
- `bazel build //wpimath/...` - Builds every target in the wpimath folder
|
||||
- `bazel test //wpiutil:wpiutil-cpp-test` - Runs only the cpp test target in the wpiutil folder
|
||||
- `bazel coverage //wpiutil/...` - (*Nix only) - Runs a code coverage report for both C++ and Java on all the targets under wpiutil
|
||||
|
||||
## User settings
|
||||
When invoking bazel, it will check if `user.bazelrc` exists for additional, user specified flags. You can use these settings to do things like always ignore buildin a specific folder, or limiting the CPU/RAM usage during a build.
|
||||
Examples:
|
||||
- `build --build_tag_filters=-wpi-example` - Do not build any targets tagged with `wpi-example` (Currently all of the targets in wpilibcExamples and wpilibjExamples contain this tag)
|
||||
- `build -c opt` - Always build optimized targets. The default compiler flags were chosen to build as fast as possible, and thus don't contain many optimizations
|
||||
- `build -k` - `-k` is analogous to the MAKE flag `--keep-going`, so the build will not stop on the first error.
|
||||
- ```
|
||||
build --local_ram_resources=HOST_RAM*.5 # Don't use more than half my RAM when building
|
||||
build --local_cpu_resources=HOST_CPUS-1 # Leave one core alone
|
||||
```
|
||||
@@ -66,6 +66,8 @@ The following build options are available:
|
||||
* This option will build the HAL and wpilibc/j during the build. The HAL is the simulation HAL, unless the external HAL options are used. The CMake build has no capability to build for the roboRIO.
|
||||
* `WITH_WPIMATH` (ON Default)
|
||||
* This option will build the wpimath library. This option must be on to build wpilib.
|
||||
* `WITH_PROTOBUF` (ON Default)
|
||||
* This option will build with the protobuf library.
|
||||
* `WITH_WPIUNITS` (`WITH_JAVA` Default)
|
||||
* This option will build the wpiunits library. This option must be on to build the Java wpimath library and requires `WITH_JAVA` to also be on.
|
||||
* `OPENCV_JAVA_INSTALL_DIR`
|
||||
@@ -17,6 +17,7 @@ Welcome to the WPILib project. This repository contains the HAL, WPILibJ, and WP
|
||||
- [Custom toolchain location](#custom-toolchain-location)
|
||||
- [Formatting/Linting](#formattinglinting)
|
||||
- [CMake](#cmake)
|
||||
- [Bazel](#bazel)
|
||||
- [Running examples in simulation](#running-examples-in-simulation)
|
||||
- [Publishing](#publishing)
|
||||
- [Structure and Organization](#structure-and-organization)
|
||||
@@ -149,7 +150,11 @@ Several files within WPILib are generated using Jinja. If a PR is opened that mo
|
||||
|
||||
### CMake
|
||||
|
||||
CMake is also supported for building. See [README-CMAKE.md](README-CMAKE.md).
|
||||
CMake is also supported for building. See [README-CMake.md](README-CMake.md).
|
||||
|
||||
### Bazel
|
||||
|
||||
Bazel is also supported for building. See [README-Bazel.md](README-Bazel.md).
|
||||
|
||||
## Running examples in simulation
|
||||
|
||||
|
||||
100
WORKSPACE
Normal file
100
WORKSPACE
Normal file
@@ -0,0 +1,100 @@
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
# Download Extra java rules
|
||||
http_archive(
|
||||
name = "rules_jvm_external",
|
||||
sha256 = "08ea921df02ffe9924123b0686dc04fd0ff875710bfadb7ad42badb931b0fd50",
|
||||
strip_prefix = "rules_jvm_external-6.1",
|
||||
url = "https://github.com/bazelbuild/rules_jvm_external/releases/download/6.1/rules_jvm_external-6.1.tar.gz",
|
||||
)
|
||||
|
||||
load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps")
|
||||
|
||||
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",
|
||||
]
|
||||
|
||||
maven_install(
|
||||
name = "maven",
|
||||
artifacts = maven_artifacts,
|
||||
repositories = [
|
||||
"https://repo1.maven.org/maven2",
|
||||
"https://frcmaven.wpi.edu/artifactory/release/",
|
||||
],
|
||||
)
|
||||
|
||||
# Download toolchains
|
||||
http_archive(
|
||||
name = "rules_bzlmodrio_toolchains",
|
||||
sha256 = "2ef1cafce7f4fd4e909bb5de8b0dc771a934646afd55d5f100ff31f6b500df98",
|
||||
url = "https://github.com/wpilibsuite/rules_bzlmodRio_toolchains/releases/download/2024-1.bcr1/rules_bzlmodRio_toolchains-2024-1.bcr1.tar.gz",
|
||||
)
|
||||
|
||||
load("@rules_bzlmodrio_toolchains//:maven_deps.bzl", "setup_legacy_setup_toolchains_dependencies")
|
||||
|
||||
setup_legacy_setup_toolchains_dependencies()
|
||||
|
||||
load("@rules_bzlmodrio_toolchains//toolchains:load_toolchains.bzl", "load_toolchains")
|
||||
|
||||
load_toolchains()
|
||||
|
||||
#
|
||||
http_archive(
|
||||
name = "rules_bzlmodrio_jdk",
|
||||
sha256 = "a00d5fa971fbcad8a17b1968cdc5350688397035e90b0cb94e040d375ecd97b4",
|
||||
url = "https://github.com/wpilibsuite/rules_bzlmodRio_jdk/releases/download/17.0.8.1-1/rules_bzlmodRio_jdk-17.0.8.1-1.tar.gz",
|
||||
)
|
||||
|
||||
load("@rules_bzlmodrio_jdk//:maven_deps.bzl", "setup_legacy_setup_jdk_dependencies")
|
||||
|
||||
setup_legacy_setup_jdk_dependencies()
|
||||
|
||||
register_toolchains(
|
||||
"@local_roborio//:macos",
|
||||
"@local_roborio//:linux",
|
||||
"@local_roborio//:windows",
|
||||
"@local_raspi_32//:macos",
|
||||
"@local_raspi_32//:linux",
|
||||
"@local_raspi_32//:windows",
|
||||
"@local_bullseye_32//:macos",
|
||||
"@local_bullseye_32//:linux",
|
||||
"@local_bullseye_32//:windows",
|
||||
"@local_bullseye_64//:macos",
|
||||
"@local_bullseye_64//:linux",
|
||||
"@local_bullseye_64//:windows",
|
||||
)
|
||||
|
||||
setup_legacy_setup_jdk_dependencies()
|
||||
|
||||
http_archive(
|
||||
name = "bzlmodrio-ni",
|
||||
sha256 = "197fceac88bf44fb8427d5e000b0083118d3346172dd2ad31eccf83a5e61b3ce",
|
||||
url = "https://github.com/wpilibsuite/bzlmodRio-ni/releases/download/2025.0.0/bzlmodRio-ni-2025.0.0.tar.gz",
|
||||
)
|
||||
|
||||
load("@bzlmodrio-ni//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_ni_cpp_dependencies")
|
||||
|
||||
setup_legacy_bzlmodrio_ni_cpp_dependencies()
|
||||
|
||||
http_archive(
|
||||
name = "bzlmodrio-opencv",
|
||||
sha256 = "5314cce05b49451a46bf3e3140fc401342e53d5f3357612ed4473e59bb616cba",
|
||||
url = "https://github.com/wpilibsuite/bzlmodRio-opencv/releases/download/2024.4.8.0-4.bcr1/bzlmodRio-opencv-2024.4.8.0-4.bcr1.tar.gz",
|
||||
)
|
||||
|
||||
load("@bzlmodrio-opencv//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_opencv_cpp_dependencies")
|
||||
|
||||
setup_legacy_bzlmodrio_opencv_cpp_dependencies()
|
||||
|
||||
load("@bzlmodrio-opencv//:maven_java_deps.bzl", "setup_legacy_bzlmodrio_opencv_java_dependencies")
|
||||
|
||||
setup_legacy_bzlmodrio_opencv_java_dependencies()
|
||||
@@ -297,7 +297,7 @@ public class AprilTagDetector implements AutoCloseable {
|
||||
* @return Results (array of AprilTagDetection)
|
||||
*/
|
||||
public AprilTagDetection[] detect(Mat img) {
|
||||
return AprilTagJNI.detect(m_native, img.cols(), img.rows(), img.cols(), img.dataAddr());
|
||||
return AprilTagJNI.detect(m_native, img.cols(), img.rows(), (int) img.step1(), img.dataAddr());
|
||||
}
|
||||
|
||||
private long m_native;
|
||||
|
||||
@@ -130,7 +130,7 @@ void AprilTagDetector::RemoveFamily(std::string_view fam) {
|
||||
apriltag_detector_remove_family(
|
||||
static_cast<apriltag_detector_t*>(m_impl),
|
||||
static_cast<apriltag_family_t*>(it->second));
|
||||
DestroyFamily(it->getKey(), it->second);
|
||||
DestroyFamily(it->first, it->second);
|
||||
m_families.erase(it);
|
||||
}
|
||||
}
|
||||
@@ -158,7 +158,7 @@ void AprilTagDetector::Destroy() {
|
||||
|
||||
void AprilTagDetector::DestroyFamilies() {
|
||||
for (auto&& entry : m_families) {
|
||||
DestroyFamily(entry.getKey(), entry.second);
|
||||
DestroyFamily(entry.first, entry.second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,34 @@ class AprilTagDetectorTest {
|
||||
return image;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecodeCropped() {
|
||||
detector.addFamily("tag16h5");
|
||||
detector.addFamily("tag36h11");
|
||||
|
||||
Mat image;
|
||||
try {
|
||||
image = loadImage("tag1_640_480.jpg");
|
||||
} catch (IOException ex) {
|
||||
fail(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pre-knowledge -- the tag is within this ROI of this particular test image
|
||||
var cropped = image.submat(100, 400, 220, 570);
|
||||
|
||||
try {
|
||||
AprilTagDetection[] results = detector.detect(cropped);
|
||||
assertEquals(1, results.length);
|
||||
assertEquals("tag36h11", results[0].getFamily());
|
||||
assertEquals(1, results[0].getId());
|
||||
assertEquals(0, results[0].getHamming());
|
||||
} finally {
|
||||
cropped.release();
|
||||
image.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecodeAndPose() {
|
||||
detector.addFamily("tag16h5");
|
||||
|
||||
@@ -21,7 +21,6 @@ plugins {
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
|
||||
id 'com.diffplug.spotless' version '6.20.0' apply false
|
||||
id 'com.github.spotbugs' version '6.0.2' apply false
|
||||
id 'com.google.protobuf' version '0.9.3' apply false
|
||||
}
|
||||
|
||||
wpilibVersioning.buildServerMode = project.hasProperty('buildServer')
|
||||
@@ -171,5 +170,5 @@ ext.getCurrentArch = {
|
||||
}
|
||||
|
||||
wrapper {
|
||||
gradleVersion = '8.5'
|
||||
gradleVersion = '8.11'
|
||||
}
|
||||
|
||||
32
cameraserver/BUILD.bazel
Normal file
32
cameraserver/BUILD.bazel
Normal file
@@ -0,0 +1,32 @@
|
||||
load("@rules_cc//cc:defs.bzl", "cc_binary")
|
||||
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
|
||||
|
||||
java_library(
|
||||
name = "cameraserver-java",
|
||||
srcs = glob(["src/main/java/**/*.java"]),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//cscore:cscore-java",
|
||||
"//hal:hal-java",
|
||||
"//ntcore:networktables-java",
|
||||
"//wpimath:wpimath-java",
|
||||
"//wpinet:wpinet-java",
|
||||
"//wpiutil:wpiutil-java",
|
||||
"@bzlmodrio-opencv//libraries/java/opencv",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "DevMain-Cpp",
|
||||
srcs = ["src/dev/native/cpp/main.cpp"],
|
||||
deps = [
|
||||
],
|
||||
)
|
||||
|
||||
java_binary(
|
||||
name = "DevMain-Java",
|
||||
srcs = ["src/dev/java/edu/wpi/first/cameraserver/DevMain.java"],
|
||||
main_class = "edu.wpi.first.cameraserver.DevMain",
|
||||
deps = [
|
||||
],
|
||||
)
|
||||
@@ -30,19 +30,6 @@ apply from: "${rootDir}/shared/opencv.gradle"
|
||||
|
||||
nativeUtils.exportsConfigs {
|
||||
cameraserver {
|
||||
x64ExcludeSymbols = [
|
||||
'_CT??_R0?AV_System_error',
|
||||
'_CT??_R0?AVexception',
|
||||
'_CT??_R0?AVfailure',
|
||||
'_CT??_R0?AVruntime_error',
|
||||
'_CT??_R0?AVsystem_error',
|
||||
'_CTA5?AVfailure',
|
||||
'_TI5?AVfailure',
|
||||
'_CT??_R0?AVout_of_range',
|
||||
'_CTA3?AVout_of_range',
|
||||
'_TI3?AVout_of_range',
|
||||
'_CT??_R0?AVbad_cast'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
16
cameraserver/multiCameraServer/BUILD.bazel
Normal file
16
cameraserver/multiCameraServer/BUILD.bazel
Normal file
@@ -0,0 +1,16 @@
|
||||
load("@rules_java//java:defs.bzl", "java_binary")
|
||||
|
||||
java_binary(
|
||||
name = "multiCameraServer-java",
|
||||
srcs = ["src/main/java/edu/wpi/Main.java"],
|
||||
main_class = "edu.wpi.Main",
|
||||
deps = [
|
||||
"//cameraserver:cameraserver-java",
|
||||
"//cscore:cscore-java",
|
||||
"//hal:hal-java",
|
||||
"//ntcore:networktables-java",
|
||||
"//wpimath:wpimath-java",
|
||||
"//wpiutil:wpiutil-java",
|
||||
"@maven//:com_google_code_gson_gson",
|
||||
],
|
||||
)
|
||||
@@ -22,7 +22,7 @@ class DefaultCameraServerShared : public frc::CameraServerShared {
|
||||
void ReportDriverStationErrorV(fmt::string_view format,
|
||||
fmt::format_args args) override {}
|
||||
std::pair<std::thread::id, bool> GetRobotMainThreadId() const override {
|
||||
return std::make_pair(std::thread::id(), false);
|
||||
return std::pair{std::thread::id(), false};
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/deprecated.h>
|
||||
|
||||
#include "cscore.h"
|
||||
#include "cscore_cv.h"
|
||||
|
||||
namespace frc {
|
||||
@@ -130,7 +130,9 @@ class CameraServer {
|
||||
*/
|
||||
template <typename T>
|
||||
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
|
||||
static cs::AxisCamera AddAxisCamera(std::initializer_list<T> hosts);
|
||||
static cs::AxisCamera AddAxisCamera(std::initializer_list<T> hosts) {
|
||||
return AddAxisCamera("Axis Camera", hosts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
@@ -185,7 +187,14 @@ class CameraServer {
|
||||
template <typename T>
|
||||
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
|
||||
static cs::AxisCamera AddAxisCamera(std::string_view name,
|
||||
std::initializer_list<T> hosts);
|
||||
std::initializer_list<T> hosts) {
|
||||
std::vector<std::string> vec;
|
||||
vec.reserve(hosts.size());
|
||||
for (const auto& host : hosts) {
|
||||
vec.emplace_back(host);
|
||||
}
|
||||
return AddAxisCamera(name, vec);
|
||||
}
|
||||
WPI_UNIGNORE_DEPRECATED
|
||||
|
||||
/**
|
||||
@@ -316,5 +325,3 @@ class CameraServer {
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
|
||||
#include "cameraserver/CameraServer.inc"
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cameraserver/CameraServer.h"
|
||||
|
||||
namespace frc {
|
||||
|
||||
WPI_IGNORE_DEPRECATED
|
||||
template <typename T>
|
||||
inline cs::AxisCamera CameraServer::AddAxisCamera(
|
||||
std::initializer_list<T> hosts) {
|
||||
return AddAxisCamera("Axis Camera", hosts);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline cs::AxisCamera CameraServer::AddAxisCamera(
|
||||
std::string_view name, std::initializer_list<T> hosts) {
|
||||
std::vector<std::string> vec;
|
||||
vec.reserve(hosts.size());
|
||||
for (const auto& host : hosts) {
|
||||
vec.emplace_back(host);
|
||||
}
|
||||
return AddAxisCamera(name, vec);
|
||||
}
|
||||
WPI_UNIGNORE_DEPRECATED
|
||||
|
||||
} // namespace frc
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "cscore.h"
|
||||
#include "cscore_cv.h"
|
||||
#include "vision/VisionPipeline.h"
|
||||
|
||||
@@ -81,17 +80,35 @@ class VisionRunnerBase {
|
||||
template <typename T>
|
||||
class VisionRunner : public VisionRunnerBase {
|
||||
public:
|
||||
/**
|
||||
* Creates a new vision runner. It will take images from the {@code
|
||||
* videoSource}, send them to the {@code pipeline}, and call the {@code
|
||||
* listener} when the pipeline has finished to alert user code when it is safe
|
||||
* to access the pipeline's outputs.
|
||||
*
|
||||
* @param videoSource The video source to use to supply images for the
|
||||
* pipeline
|
||||
* @param pipeline The vision pipeline to run
|
||||
* @param listener A function to call after the pipeline has finished
|
||||
* running
|
||||
*/
|
||||
VisionRunner(cs::VideoSource videoSource, T* pipeline,
|
||||
std::function<void(T&)> listener);
|
||||
std::function<void(T&)> listener)
|
||||
: VisionRunnerBase(videoSource),
|
||||
m_pipeline(pipeline),
|
||||
m_listener(listener) {}
|
||||
|
||||
virtual ~VisionRunner() = default;
|
||||
|
||||
protected:
|
||||
void DoProcess(cv::Mat& image) override;
|
||||
void DoProcess(cv::Mat& image) override {
|
||||
m_pipeline->Process(image);
|
||||
m_listener(*m_pipeline);
|
||||
}
|
||||
|
||||
private:
|
||||
T* m_pipeline;
|
||||
std::function<void(T&)> m_listener;
|
||||
};
|
||||
} // namespace frc
|
||||
|
||||
#include "VisionRunner.inc"
|
||||
} // namespace frc
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "vision/VisionRunner.h"
|
||||
|
||||
namespace frc {
|
||||
|
||||
/**
|
||||
* Creates a new vision runner. It will take images from the {@code
|
||||
* videoSource}, send them to the {@code pipeline}, and call the {@code
|
||||
* listener} when the pipeline has finished to alert user code when it is safe
|
||||
* to access the pipeline's outputs.
|
||||
*
|
||||
* @param videoSource The video source to use to supply images for the pipeline
|
||||
* @param pipeline The vision pipeline to run
|
||||
* @param listener A function to call after the pipeline has finished running
|
||||
*/
|
||||
template <typename T>
|
||||
VisionRunner<T>::VisionRunner(cs::VideoSource videoSource, T* pipeline,
|
||||
std::function<void(T&)> listener)
|
||||
: VisionRunnerBase(videoSource),
|
||||
m_pipeline(pipeline),
|
||||
m_listener(listener) {}
|
||||
|
||||
template <typename T>
|
||||
void VisionRunner<T>::DoProcess(cv::Mat& image) {
|
||||
m_pipeline->Process(image);
|
||||
m_listener(*m_pipeline);
|
||||
}
|
||||
|
||||
} // namespace frc
|
||||
144
cmake/modules/AddDoxygenDocs.cmake
Normal file
144
cmake/modules/AddDoxygenDocs.cmake
Normal file
@@ -0,0 +1,144 @@
|
||||
macro(add_doxygen_docs)
|
||||
set(dirs
|
||||
apriltag
|
||||
cameraserver
|
||||
cscore
|
||||
fieldImages
|
||||
hal
|
||||
ntcore
|
||||
romiVendordep
|
||||
wpilibc
|
||||
wpilibNewCommands
|
||||
wpimath
|
||||
wpinet
|
||||
wpiutil
|
||||
xrpVendordep
|
||||
)
|
||||
foreach(dir ${dirs})
|
||||
list(APPEND docs_dirs ${dir}/src/main/native/include)
|
||||
file(GLOB dirs ${dir}/src/main/native/thirdparty/*/include)
|
||||
list(FILTER dirs EXCLUDE REGEX eigen|protobuf)
|
||||
set(DOXYGEN_EXCLUDE_PATTERNS "*.pb.h" "**/.clang-tidy" "**/.clang-format")
|
||||
|
||||
if(DOCS_WARNINGS_AS_ERRORS)
|
||||
set(DOXYGEN_WARN_AS_ERROR "FAIL_ON_WARNINGS_PRINT")
|
||||
list(FILTER dirs EXCLUDE REGEX fmt|memory|units)
|
||||
list(
|
||||
APPEND
|
||||
DOXYGEN_EXCLUDE_PATTERNS
|
||||
# apriltag
|
||||
"apriltag_pose.h"
|
||||
# llvm
|
||||
"wpi/AlignOf.h"
|
||||
"wpi/Casting.h"
|
||||
"wpi/Chrono.h"
|
||||
"wpi/Compiler.h"
|
||||
"wpi/ConvertUTF.h"
|
||||
"wpi/DenseMap.h"
|
||||
"wpi/DenseMapInfo.h"
|
||||
"wpi/Endian.h"
|
||||
"wpi/EpochTracker.h"
|
||||
"wpi/Errc.h"
|
||||
"wpi/Errno.h"
|
||||
"wpi/ErrorHandling.h"
|
||||
"wpi/bit.h"
|
||||
"wpi/fs.h"
|
||||
"wpi/FunctionExtras.h"
|
||||
"wpi/function_ref.h"
|
||||
"wpi/Hashing.h"
|
||||
"wpi/iterator.h"
|
||||
"wpi/iterator_range.h"
|
||||
"wpi/ManagedStatic.h"
|
||||
"wpi/MapVector.h"
|
||||
"wpi/MathExtras.h"
|
||||
"wpi/MemAlloc.h"
|
||||
"wpi/PointerIntPair.h"
|
||||
"wpi/PointerLikeTypeTraits.h"
|
||||
"wpi/PointerUnion.h"
|
||||
"wpi/raw_os_ostream.h"
|
||||
"wpi/raw_ostream.h"
|
||||
"wpi/SmallPtrSet.h"
|
||||
"wpi/SmallSet.h"
|
||||
"wpi/SmallString.h"
|
||||
"wpi/SmallVector.h"
|
||||
"wpi/StringExtras.h"
|
||||
"wpi/StringMap.h"
|
||||
"wpi/SwapByteOrder.h"
|
||||
"wpi/type_traits.h"
|
||||
"wpi/VersionTuple.h"
|
||||
"wpi/WindowsError.h"
|
||||
# libuv
|
||||
"uv.h"
|
||||
"uv/**"
|
||||
# json
|
||||
"wpi/adl_serializer.h"
|
||||
"wpi/byte_container_with_subtype.h"
|
||||
"wpi/json.h"
|
||||
"wpi/json_fwd.h"
|
||||
"wpi/ordered_map.h"
|
||||
# mpack
|
||||
"wpi/mpack.h"
|
||||
)
|
||||
endif()
|
||||
list(APPEND docs_dirs ${dirs})
|
||||
list(APPEND docs_dirs ${dir}/src/generated/main/native/include)
|
||||
endforeach()
|
||||
|
||||
set(DOXYGEN_CASE_SENSE_NAMES false)
|
||||
set(DOXYGEN_EXTENSION_MAPPING inc=C++ no_extension=C++)
|
||||
set(DOXYGEN_EXTRACT_ALL true)
|
||||
set(DOXYGEN_EXTRACT_STATIC true)
|
||||
set(DOXYGEN_FILE_PATTERNS "*")
|
||||
set(DOXYGEN_FULL_PATH_NAMES true)
|
||||
set(DOXYGEN_FULL_SIDEBAR false)
|
||||
set(DOXYGEN_GENERATE_HTML true)
|
||||
set(DOXYGEN_GENERATE_LATEX false)
|
||||
set(DOXYGEN_GENERATE_TREEVIEW true)
|
||||
set(DOXYGEN_HTML_COLORSTYLE "LIGHT")
|
||||
set(DOXYGEN_HTML_EXTRA_STYLESHEET docs/theme.css)
|
||||
set(DOXYGEN_JAVADOC_AUTOBRIEF true)
|
||||
set(DOXYGEN_ALIASES
|
||||
"effects=\\par <i>Effects:</i>^^"
|
||||
"notes=\\par <i>Notes:</i>^^"
|
||||
"requires=\\par <i>Requires:</i>^^"
|
||||
"requiredbe=\\par <i>Required Behavior:</i>^^"
|
||||
"concept{2}=<a href=\"md_doc_concepts.html#1\">2</a>"
|
||||
"defaultbe=\\par <i>Default Behavior:</i>^^"
|
||||
)
|
||||
set(DOXYGEN_PROJECT_NAME WPILibC++)
|
||||
set(DOXYGEN_PROJECT_NUMBER version)
|
||||
set(DOXYGEN_PROJECT_LOGO wpiutil/src/main/native/resources/wpilib-128.png)
|
||||
set(DOXYGEN_QUIET true)
|
||||
set(DOXYGEN_RECURSIVE true)
|
||||
set(DOXYGEN_STRIP_CODE_COMMENTS false)
|
||||
set(DOXYGEN_STRIP_FROM_PATH ${docs_dirs})
|
||||
set(DOXYGEN_STRIP_FROM_INC_PATH ${docs_dirs})
|
||||
set(DOXYGEN_TIMESTAMP "DATETIME")
|
||||
set(DOXYGEN_USE_MATHJAX true)
|
||||
set(DOXYGEN_WARNINGS false)
|
||||
set(DOXYGEN_WARN_IF_INCOMPLETE_DOC true)
|
||||
set(DOXYGEN_WARN_IF_UNDOCUMENTED false)
|
||||
set(DOXYGEN_WARN_NO_PARAMDOC true)
|
||||
|
||||
set(DOXYGEN_ENABLE_PREPROCESSING true)
|
||||
set(DOXYGEN_MACRO_EXPANSION true)
|
||||
set(DOXYGEN_EXPAND_ONLY_PREDEF true)
|
||||
set(DOXYGEN_PREDEFINED
|
||||
"__cplusplus"
|
||||
"HAL_ENUM(name)=enum name : int32_t"
|
||||
"DOXYGEN"
|
||||
"WPI_NOEXCEPT:=noexcept"
|
||||
"WPI_SFINAE(x):="
|
||||
"WPI_REQUIRES(x):="
|
||||
"WPI_REQUIRES_RET(...):="
|
||||
"WPI_ENABLE_IF(...):="
|
||||
"WPI_CONSTEXPR:=constexpr"
|
||||
"WPI_CONSTEXPR_FNC:=constexpr"
|
||||
"WPI_IMPL_DEFINED(...):=implementation_defined"
|
||||
"WPI_EBO(...):="
|
||||
)
|
||||
execute_process(COMMAND git describe OUTPUT_VARIABLE version)
|
||||
string(SUBSTRING ${version} 1 -1 version)
|
||||
set(DOXYGEN_PROJECT_NUMBER ${version})
|
||||
doxygen_add_docs(docs ${docs_dirs})
|
||||
endmacro()
|
||||
@@ -55,9 +55,4 @@ macro(wpilib_target_warnings target)
|
||||
)
|
||||
target_compile_options(${target} PRIVATE -gz=zlib)
|
||||
endif()
|
||||
|
||||
# Disable std::mutex constexpr constructor on MSVC
|
||||
if(MSVC)
|
||||
target_compile_options(${target} PRIVATE /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
function(wpi_protobuf_generate)
|
||||
set(_singleargs PROTOC_OUT_DIR PLUGIN DEPENDENCIES)
|
||||
if(COMMAND target_sources)
|
||||
list(APPEND _singleargs TARGET)
|
||||
endif()
|
||||
set(_multiargs PROTOS)
|
||||
|
||||
cmake_parse_arguments(
|
||||
wpi_protobuf_generate
|
||||
"${_options}"
|
||||
"${_singleargs}"
|
||||
"${_multiargs}"
|
||||
"${ARGN}"
|
||||
)
|
||||
|
||||
if(NOT wpi_protobuf_generate_PROTOS)
|
||||
message(SEND_ERROR "Error: protobuf_generate called without any targets or source files")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT wpi_protobuf_generate_TARGET)
|
||||
message(SEND_ERROR "Error: wpi_protobuf_generate called without a target")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT wpi_protobuf_generate_PROTOC_OUT_DIR)
|
||||
message(SEND_ERROR "Error: protobuf_generate called without a protoc out directory")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT wpi_protobuf_generate_PLUGIN)
|
||||
message(SEND_ERROR "Error: wpi_protobuf_generate called without a plugin")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(_generate_extensions .pb.h .pb.cc)
|
||||
|
||||
# Create an include path for each file specified
|
||||
foreach(_file ${wpi_protobuf_generate_PROTOS})
|
||||
get_filename_component(_abs_file ${_file} ABSOLUTE)
|
||||
get_filename_component(_abs_dir ${_abs_file} DIRECTORY)
|
||||
list(FIND _protobuf_include_path ${_abs_dir} _contains_already)
|
||||
if(${_contains_already} EQUAL -1)
|
||||
list(APPEND _protobuf_include_path -I ${_abs_dir})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(_generated_srcs_all)
|
||||
foreach(_proto ${wpi_protobuf_generate_PROTOS})
|
||||
get_filename_component(_abs_file ${_proto} ABSOLUTE)
|
||||
get_filename_component(_abs_dir ${_abs_file} DIRECTORY)
|
||||
get_filename_component(_basename ${_proto} NAME_WLE)
|
||||
file(RELATIVE_PATH _rel_dir ${CMAKE_CURRENT_SOURCE_DIR} ${_abs_dir})
|
||||
|
||||
set(_possible_rel_dir)
|
||||
|
||||
set(_generated_srcs)
|
||||
foreach(_ext ${_generate_extensions})
|
||||
list(
|
||||
APPEND
|
||||
_generated_srcs
|
||||
"${wpi_protobuf_generate_PROTOC_OUT_DIR}/${_possible_rel_dir}${_basename}${_ext}"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
list(APPEND _generated_srcs_all ${_generated_srcs})
|
||||
|
||||
set(_comment "Running WPILib protocol buffer compiler on ${_proto}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${_generated_srcs}
|
||||
COMMAND protobuf::protoc
|
||||
ARGS
|
||||
--cpp_out ${wpi_protobuf_generate_PROTOC_OUT_DIR} --wpilib_out
|
||||
${wpi_protobuf_generate_PROTOC_OUT_DIR}
|
||||
--plugin=protoc-gen-wpilib=${wpi_protobuf_generate_PLUGIN} ${_protobuf_include_path}
|
||||
${_abs_file}
|
||||
DEPENDS
|
||||
${_abs_file}
|
||||
protobuf::protoc
|
||||
${wpi_protobuf_generate_DEPENDENCIES}
|
||||
${wpi_protobuf_generate_PLUGIN}
|
||||
COMMENT ${_comment}
|
||||
VERBATIM
|
||||
)
|
||||
endforeach()
|
||||
|
||||
set_source_files_properties(${_generated_srcs_all} PROPERTIES GENERATED TRUE)
|
||||
if(wpi_protobuf_generate_TARGET)
|
||||
target_sources(${wpi_protobuf_generate_TARGET} PRIVATE ${_generated_srcs_all})
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -1,4 +0,0 @@
|
||||
set(GCC_COMPILER_VERSION "" CACHE STRING "GCC Compiler version")
|
||||
set(GNU_MACHINE "arm-frc2022-linux-gnueabi" CACHE STRING "GNU compiler triple")
|
||||
set(SOFTFP yes)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/arm.toolchain.cmake")
|
||||
@@ -1,98 +0,0 @@
|
||||
set(GCC_COMPILER_VERSION "" CACHE STRING "GCC Compiler version")
|
||||
set(GNU_MACHINE "arm-raspbian10-linux-gnueabi" CACHE STRING "GNU compiler triple")
|
||||
|
||||
if(COMMAND toolchain_save_config)
|
||||
return() # prevent recursive call
|
||||
endif()
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
if(NOT DEFINED CMAKE_SYSTEM_PROCESSOR)
|
||||
set(CMAKE_SYSTEM_PROCESSOR arm)
|
||||
else()
|
||||
#message("CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/opencv/platforms/linux/gnu.toolchain.cmake")
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm AND NOT ARM_IGNORE_FP)
|
||||
set(FLOAT_ABI_SUFFIX "")
|
||||
if(NOT SOFTFP)
|
||||
set(FLOAT_ABI_SUFFIX "hf")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT "x${GCC_COMPILER_VERSION}" STREQUAL "x")
|
||||
set(__GCC_VER_SUFFIX "-${GCC_COMPILER_VERSION}")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_C_COMPILER)
|
||||
find_program(CMAKE_C_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-gcc${__GCC_VER_SUFFIX})
|
||||
else()
|
||||
#message(WARNING "CMAKE_C_COMPILER=${CMAKE_C_COMPILER} is defined")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_CXX_COMPILER)
|
||||
find_program(CMAKE_CXX_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-g++${__GCC_VER_SUFFIX})
|
||||
else()
|
||||
#message(WARNING "CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} is defined")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_LINKER)
|
||||
find_program(CMAKE_LINKER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld)
|
||||
else()
|
||||
#message(WARNING "CMAKE_LINKER=${CMAKE_LINKER} is defined")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_AR)
|
||||
find_program(CMAKE_AR NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar)
|
||||
else()
|
||||
#message(WARNING "CMAKE_AR=${CMAKE_AR} is defined")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED ARM_LINUX_SYSROOT AND DEFINED GNU_MACHINE)
|
||||
set(ARM_LINUX_SYSROOT /usr/${GNU_MACHINE}${FLOAT_ABI_SUFFIX})
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_CXX_FLAGS)
|
||||
set(CMAKE_CXX_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_C_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "" CACHE INTERNAL "")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,nocopyreloc")
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
|
||||
set(ARM_LINKER_FLAGS "-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
|
||||
set(ARM_LINKER_FLAGS "-Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
|
||||
endif()
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
else()
|
||||
#message(WARNING "CMAKE_CXX_FLAGS='${CMAKE_CXX_FLAGS}' is defined")
|
||||
endif()
|
||||
|
||||
if(USE_NEON)
|
||||
message(WARNING "You use obsolete variable USE_NEON to enable NEON instruction set. Use -DENABLE_NEON=ON instead." )
|
||||
set(ENABLE_NEON TRUE)
|
||||
elseif(USE_VFPV3)
|
||||
message(WARNING "You use obsolete variable USE_VFPV3 to enable VFPV3 instruction set. Use -DENABLE_VFPV3=ON instead." )
|
||||
set(ENABLE_VFPV3 TRUE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${ARM_LINUX_SYSROOT})
|
||||
|
||||
if(EXISTS ${CUDA_TOOLKIT_ROOT_DIR})
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${CUDA_TOOLKIT_ROOT_DIR})
|
||||
endif()
|
||||
|
||||
set(TOOLCHAIN_CONFIG_VARS ${TOOLCHAIN_CONFIG_VARS}
|
||||
ARM_LINUX_SYSROOT
|
||||
ENABLE_NEON
|
||||
ENABLE_VFPV3
|
||||
CUDA_TOOLKIT_ROOT_DIR
|
||||
)
|
||||
toolchain_save_config()
|
||||
@@ -1,97 +0,0 @@
|
||||
if(COMMAND toolchain_save_config)
|
||||
return() # prevent recursive call
|
||||
endif()
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
if(NOT DEFINED CMAKE_SYSTEM_PROCESSOR)
|
||||
set(CMAKE_SYSTEM_PROCESSOR arm)
|
||||
else()
|
||||
#message("CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/gnu.toolchain.cmake")
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm AND NOT ARM_IGNORE_FP)
|
||||
set(FLOAT_ABI_SUFFIX "")
|
||||
if(NOT SOFTFP)
|
||||
set(FLOAT_ABI_SUFFIX "hf")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT "x${GCC_COMPILER_VERSION}" STREQUAL "x")
|
||||
set(__GCC_VER_SUFFIX "-${GCC_COMPILER_VERSION}")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_C_COMPILER)
|
||||
find_program(CMAKE_C_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-gcc${__GCC_VER_SUFFIX})
|
||||
else()
|
||||
#message(WARNING "CMAKE_C_COMPILER=${CMAKE_C_COMPILER} is defined")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_CXX_COMPILER)
|
||||
find_program(CMAKE_CXX_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-g++${__GCC_VER_SUFFIX})
|
||||
else()
|
||||
#message(WARNING "CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} is defined")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_LINKER)
|
||||
find_program(CMAKE_LINKER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld)
|
||||
else()
|
||||
#message(WARNING "CMAKE_LINKER=${CMAKE_LINKER} is defined")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_AR)
|
||||
find_program(CMAKE_AR NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar)
|
||||
else()
|
||||
#message(WARNING "CMAKE_AR=${CMAKE_AR} is defined")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED ARM_LINUX_SYSROOT AND DEFINED GNU_MACHINE)
|
||||
set(ARM_LINUX_SYSROOT /usr/${GNU_MACHINE}${FLOAT_ABI_SUFFIX})
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_CXX_FLAGS)
|
||||
set(CMAKE_CXX_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_C_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "" CACHE INTERNAL "")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
|
||||
set(CMAKE_CXX_FLAGS "-mthumb ${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "-mthumb ${CMAKE_C_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,nocopyreloc")
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
|
||||
set(ARM_LINKER_FLAGS "-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
|
||||
set(ARM_LINKER_FLAGS "-Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
|
||||
endif()
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
else()
|
||||
#message(WARNING "CMAKE_CXX_FLAGS='${CMAKE_CXX_FLAGS}' is defined")
|
||||
endif()
|
||||
|
||||
if(USE_NEON)
|
||||
message(WARNING "You use obsolete variable USE_NEON to enable NEON instruction set. Use -DENABLE_NEON=ON instead." )
|
||||
set(ENABLE_NEON TRUE)
|
||||
elseif(USE_VFPV3)
|
||||
message(WARNING "You use obsolete variable USE_VFPV3 to enable VFPV3 instruction set. Use -DENABLE_VFPV3=ON instead." )
|
||||
set(ENABLE_VFPV3 TRUE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${ARM_LINUX_SYSROOT})
|
||||
|
||||
if(EXISTS ${CUDA_TOOLKIT_ROOT_DIR})
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${CUDA_TOOLKIT_ROOT_DIR})
|
||||
endif()
|
||||
|
||||
set(TOOLCHAIN_CONFIG_VARS ${TOOLCHAIN_CONFIG_VARS}
|
||||
ARM_LINUX_SYSROOT
|
||||
ENABLE_NEON
|
||||
ENABLE_VFPV3
|
||||
CUDA_TOOLKIT_ROOT_DIR
|
||||
)
|
||||
toolchain_save_config()
|
||||
@@ -1,134 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.11)
|
||||
|
||||
# load settings in case of "try compile"
|
||||
set(TOOLCHAIN_CONFIG_FILE "${WPILIB_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/toolchain.config.cmake")
|
||||
get_property(__IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
|
||||
if(__IN_TRY_COMPILE)
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../toolchain.config.cmake" OPTIONAL) # WPILIB_BINARY_DIR is different
|
||||
macro(toolchain_save_config)
|
||||
# nothing
|
||||
endmacro()
|
||||
else()
|
||||
macro(toolchain_save_config)
|
||||
set(__config "#message(\"Load TOOLCHAIN config...\")\n")
|
||||
get_cmake_property(__variableNames VARIABLES)
|
||||
set(__vars_list ${ARGN})
|
||||
list(APPEND __vars_list
|
||||
${TOOLCHAIN_CONFIG_VARS}
|
||||
CMAKE_SYSTEM_NAME
|
||||
CMAKE_SYSTEM_VERSION
|
||||
CMAKE_SYSTEM_PROCESSOR
|
||||
CMAKE_C_COMPILER
|
||||
CMAKE_CXX_COMPILER
|
||||
CMAKE_C_FLAGS
|
||||
CMAKE_CXX_FLAGS
|
||||
CMAKE_SHARED_LINKER_FLAGS
|
||||
CMAKE_MODULE_LINKER_FLAGS
|
||||
CMAKE_EXE_LINKER_FLAGS
|
||||
CMAKE_SKIP_RPATH
|
||||
CMAKE_FIND_ROOT_PATH
|
||||
GCC_COMPILER_VERSION
|
||||
)
|
||||
foreach(__var ${__variableNames})
|
||||
foreach(_v ${__vars_list})
|
||||
if("x${__var}" STREQUAL "x${_v}")
|
||||
if(${__var} MATCHES " ")
|
||||
set(__config "${__config}set(${__var} \"${${__var}}\")\n")
|
||||
else()
|
||||
set(__config "${__config}set(${__var} ${${__var}})\n")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
if(EXISTS "${TOOLCHAIN_CONFIG_FILE}")
|
||||
file(READ "${TOOLCHAIN_CONFIG_FILE}" __config_old)
|
||||
endif()
|
||||
if("${__config_old}" STREQUAL "${__config}")
|
||||
# nothing
|
||||
else()
|
||||
#message("Update TOOLCHAIN config: ${__config}")
|
||||
file(WRITE "${TOOLCHAIN_CONFIG_FILE}" "${__config}")
|
||||
endif()
|
||||
unset(__config)
|
||||
unset(__config_old)
|
||||
unset(__vars_list)
|
||||
unset(__variableNames)
|
||||
endmacro()
|
||||
endif() # IN_TRY_COMPILE
|
||||
|
||||
if(NOT CMAKE_FIND_ROOT_PATH_MODE_LIBRARY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_FIND_ROOT_PATH_MODE_INCLUDE)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_FIND_ROOT_PATH_MODE_PACKAGE)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_FIND_ROOT_PATH_MODE_PROGRAM)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
endif()
|
||||
|
||||
macro(__cmake_find_root_save_and_reset)
|
||||
foreach(v
|
||||
CMAKE_FIND_ROOT_PATH_MODE_LIBRARY
|
||||
CMAKE_FIND_ROOT_PATH_MODE_INCLUDE
|
||||
CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
|
||||
CMAKE_FIND_ROOT_PATH_MODE_PROGRAM
|
||||
)
|
||||
set(__save_${v} ${${v}})
|
||||
set(${v} NEVER)
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
macro(__cmake_find_root_restore)
|
||||
foreach(v
|
||||
CMAKE_FIND_ROOT_PATH_MODE_LIBRARY
|
||||
CMAKE_FIND_ROOT_PATH_MODE_INCLUDE
|
||||
CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
|
||||
CMAKE_FIND_ROOT_PATH_MODE_PROGRAM
|
||||
)
|
||||
set(${v} ${__save_${v}})
|
||||
unset(__save_${v})
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
|
||||
# macro to find programs on the host OS
|
||||
macro(find_host_program)
|
||||
__cmake_find_root_save_and_reset()
|
||||
if(CMAKE_HOST_WIN32)
|
||||
SET(WIN32 1)
|
||||
SET(UNIX)
|
||||
elseif(CMAKE_HOST_APPLE)
|
||||
SET(APPLE 1)
|
||||
SET(UNIX)
|
||||
endif()
|
||||
find_program(${ARGN})
|
||||
SET(WIN32)
|
||||
SET(APPLE)
|
||||
SET(UNIX 1)
|
||||
__cmake_find_root_restore()
|
||||
endmacro()
|
||||
|
||||
# macro to find packages on the host OS
|
||||
macro(find_host_package)
|
||||
__cmake_find_root_save_and_reset()
|
||||
if(CMAKE_HOST_WIN32)
|
||||
SET(WIN32 1)
|
||||
SET(UNIX)
|
||||
elseif(CMAKE_HOST_APPLE)
|
||||
SET(APPLE 1)
|
||||
SET(UNIX)
|
||||
endif()
|
||||
find_package(${ARGN})
|
||||
SET(WIN32)
|
||||
SET(APPLE)
|
||||
SET(UNIX 1)
|
||||
__cmake_find_root_restore()
|
||||
endmacro()
|
||||
|
||||
set(CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries.")
|
||||
21
cscore/BUILD.bazel
Normal file
21
cscore/BUILD.bazel
Normal file
@@ -0,0 +1,21 @@
|
||||
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
|
||||
|
||||
java_library(
|
||||
name = "cscore-java",
|
||||
srcs = glob(["src/main/java/**/*.java"]),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//wpiutil:wpiutil-java",
|
||||
"@bzlmodrio-opencv//libraries/java/opencv",
|
||||
],
|
||||
)
|
||||
|
||||
java_binary(
|
||||
name = "DevMain-Java",
|
||||
srcs = ["src/dev/java/edu/wpi/first/cscore/DevMain.java"],
|
||||
main_class = "edu.wpi.first.cscore.DevMain",
|
||||
deps = [
|
||||
":cscore-java",
|
||||
"//wpiutil:wpiutil-java",
|
||||
],
|
||||
)
|
||||
@@ -164,19 +164,6 @@ run {
|
||||
|
||||
nativeUtils.exportsConfigs {
|
||||
cscore {
|
||||
x64ExcludeSymbols = [
|
||||
'_CT??_R0?AV_System_error',
|
||||
'_CT??_R0?AVexception',
|
||||
'_CT??_R0?AVfailure',
|
||||
'_CT??_R0?AVruntime_error',
|
||||
'_CT??_R0?AVsystem_error',
|
||||
'_CTA5?AVfailure',
|
||||
'_TI5?AVfailure',
|
||||
'_CT??_R0?AVout_of_range',
|
||||
'_CTA3?AVout_of_range',
|
||||
'_TI3?AVout_of_range',
|
||||
'_CT??_R0?AVbad_cast'
|
||||
]
|
||||
}
|
||||
cscoreJNI {
|
||||
x64SymbolFilter = symbolFilter
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/MemAlloc.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpi/timestamp.h>
|
||||
@@ -294,12 +295,27 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::raw_istream& is,
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int contentLength = 0;
|
||||
int width, height;
|
||||
if (auto v = wpi::parse_integer<unsigned int>(contentLengthBuf, 10)) {
|
||||
contentLength = v.value();
|
||||
// We know how big it is! Just get a frame of the right size and read
|
||||
// the data directly into it.
|
||||
unsigned int contentLength = v.value();
|
||||
auto image =
|
||||
AllocImage(VideoMode::PixelFormat::kMJPEG, 0, 0, contentLength);
|
||||
is.read(image->data(), contentLength);
|
||||
if (!m_active || is.has_error()) {
|
||||
return false;
|
||||
}
|
||||
if (!GetJpegSize(image->str(), &width, &height)) {
|
||||
SWARNING("did not receive a JPEG image");
|
||||
PutError("did not receive a JPEG image", wpi::Now());
|
||||
return false;
|
||||
}
|
||||
image->width = width;
|
||||
image->height = height;
|
||||
PutFrame(std::move(image), wpi::Now());
|
||||
} else {
|
||||
// Ugh, no Content-Length? Read the blocks of the JPEG file.
|
||||
int width, height;
|
||||
if (!ReadJpeg(is, imageBuf, &width, &height)) {
|
||||
SWARNING("did not receive a JPEG image");
|
||||
PutError("did not receive a JPEG image", wpi::Now());
|
||||
@@ -307,27 +323,18 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::raw_istream& is,
|
||||
}
|
||||
PutFrame(VideoMode::PixelFormat::kMJPEG, width, height, imageBuf,
|
||||
wpi::Now());
|
||||
++m_frameCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We know how big it is! Just get a frame of the right size and read
|
||||
// the data directly into it.
|
||||
auto image = AllocImage(VideoMode::PixelFormat::kMJPEG, 0, 0, contentLength);
|
||||
is.read(image->data(), contentLength);
|
||||
if (!m_active || is.has_error()) {
|
||||
return false;
|
||||
}
|
||||
int width, height;
|
||||
if (!GetJpegSize(image->str(), &width, &height)) {
|
||||
SWARNING("did not receive a JPEG image");
|
||||
PutError("did not receive a JPEG image", wpi::Now());
|
||||
return false;
|
||||
}
|
||||
image->width = width;
|
||||
image->height = height;
|
||||
PutFrame(std::move(image), wpi::Now());
|
||||
++m_frameCount;
|
||||
|
||||
// update video mode if not set
|
||||
std::scoped_lock lock(m_mutex);
|
||||
if (m_mode.pixelFormat != VideoMode::PixelFormat::kMJPEG ||
|
||||
m_mode.width == 0 || m_mode.height == 0) {
|
||||
m_mode.pixelFormat = VideoMode::PixelFormat::kMJPEG;
|
||||
m_mode.width = width;
|
||||
m_mode.height = height;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -518,6 +525,14 @@ bool HttpCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) {
|
||||
}
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_mode = mode;
|
||||
m_streamSettings.clear();
|
||||
if (mode.width != 0 && mode.height != 0) {
|
||||
m_streamSettings["resolution"] =
|
||||
fmt::format("{}x{}", mode.width, mode.height);
|
||||
}
|
||||
if (mode.fps != 0) {
|
||||
m_streamSettings["fps"] = fmt::format("{}", mode.fps);
|
||||
}
|
||||
m_streamSettingsUpdated = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/StringMap.h>
|
||||
#include <wpi/condition_variable.h>
|
||||
#include <wpi/raw_istream.h>
|
||||
@@ -136,10 +135,10 @@ class HttpCameraImpl : public SourceImpl {
|
||||
|
||||
wpi::condition_variable m_sinkEnabledCond;
|
||||
|
||||
wpi::StringMap<wpi::SmallString<16>> m_settings;
|
||||
wpi::StringMap<std::string> m_settings;
|
||||
wpi::condition_variable m_settingsCond;
|
||||
|
||||
wpi::StringMap<wpi::SmallString<16>> m_streamSettings;
|
||||
wpi::StringMap<std::string> m_streamSettings;
|
||||
std::atomic_bool m_streamSettingsUpdated{false};
|
||||
|
||||
wpi::condition_variable m_monitorCond;
|
||||
|
||||
@@ -98,6 +98,9 @@ void Instance::DestroySource(CS_Source handle) {
|
||||
|
||||
void Instance::DestroySink(CS_Sink handle) {
|
||||
if (auto data = m_sinks.Free(handle)) {
|
||||
if (auto source = data->sink->GetSource()) {
|
||||
source->Wakeup();
|
||||
}
|
||||
notifier.NotifySink(data->sink->GetName(), handle, CS_SINK_DESTROYED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,8 @@ Frame SourceImpl::GetCurFrame() {
|
||||
Frame SourceImpl::GetNextFrame() {
|
||||
std::unique_lock lock{m_frameMutex};
|
||||
auto oldTime = m_frame.GetTime();
|
||||
m_frameCv.wait(lock, [=, this] { return m_frame.GetTime() != oldTime; });
|
||||
m_frameCv.wait(
|
||||
lock, [=, this] { return oldTime == 0 || m_frame.GetTime() != oldTime; });
|
||||
return m_frame;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class Telemetry::Thread : public wpi::SafeThread {
|
||||
|
||||
int64_t Telemetry::Thread::GetValue(CS_Handle handle, CS_TelemetryKind kind,
|
||||
CS_Status* status) {
|
||||
auto it = m_user.find(std::make_pair(handle, static_cast<int>(kind)));
|
||||
auto it = m_user.find(std::pair{handle, static_cast<int>(kind)});
|
||||
if (it == m_user.end()) {
|
||||
*status = CS_EMPTY_VALUE;
|
||||
return 0;
|
||||
@@ -136,8 +136,8 @@ void Telemetry::RecordSourceBytes(const SourceImpl& source, int quantity) {
|
||||
return;
|
||||
}
|
||||
auto handleData = Instance::GetInstance().FindSource(source);
|
||||
thr->m_current[std::make_pair(Handle{handleData.first, Handle::kSource},
|
||||
static_cast<int>(CS_SOURCE_BYTES_RECEIVED))] +=
|
||||
thr->m_current[std::pair{Handle{handleData.first, Handle::kSource},
|
||||
static_cast<int>(CS_SOURCE_BYTES_RECEIVED)}] +=
|
||||
quantity;
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ void Telemetry::RecordSourceFrames(const SourceImpl& source, int quantity) {
|
||||
return;
|
||||
}
|
||||
auto handleData = Instance::GetInstance().FindSource(source);
|
||||
thr->m_current[std::make_pair(Handle{handleData.first, Handle::kSource},
|
||||
static_cast<int>(CS_SOURCE_FRAMES_RECEIVED))] +=
|
||||
thr->m_current[std::pair{Handle{handleData.first, Handle::kSource},
|
||||
static_cast<int>(CS_SOURCE_FRAMES_RECEIVED)}] +=
|
||||
quantity;
|
||||
}
|
||||
|
||||
@@ -183,10 +183,10 @@ UnlimitedHandleResource<THandle, TStruct, typeValue, TMutex>::FindIf(F func) {
|
||||
for (size_t i = 0; i < m_structures.size(); i++) {
|
||||
auto& structure = m_structures[i];
|
||||
if (structure != nullptr && func(*structure)) {
|
||||
return std::make_pair(MakeHandle(i), structure);
|
||||
return std::pair{MakeHandle(i), structure};
|
||||
}
|
||||
}
|
||||
return std::make_pair(0, nullptr);
|
||||
return std::pair{0, nullptr};
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,649 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#ifndef CSCORE_CSCORE_OO_INC_
|
||||
#define CSCORE_CSCORE_OO_INC_
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/deprecated.h>
|
||||
|
||||
#include "cscore_oo.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
inline std::string VideoProperty::GetName() const {
|
||||
m_status = 0;
|
||||
return GetPropertyName(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline int VideoProperty::Get() const {
|
||||
m_status = 0;
|
||||
return GetProperty(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline void VideoProperty::Set(int value) {
|
||||
m_status = 0;
|
||||
SetProperty(m_handle, value, &m_status);
|
||||
}
|
||||
|
||||
inline int VideoProperty::GetMin() const {
|
||||
m_status = 0;
|
||||
return GetPropertyMin(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline int VideoProperty::GetMax() const {
|
||||
m_status = 0;
|
||||
return GetPropertyMax(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline int VideoProperty::GetStep() const {
|
||||
m_status = 0;
|
||||
return GetPropertyStep(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline int VideoProperty::GetDefault() const {
|
||||
m_status = 0;
|
||||
return GetPropertyDefault(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline std::string VideoProperty::GetString() const {
|
||||
m_status = 0;
|
||||
return GetStringProperty(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline std::string_view VideoProperty::GetString(
|
||||
wpi::SmallVectorImpl<char>& buf) const {
|
||||
m_status = 0;
|
||||
return GetStringProperty(m_handle, buf, &m_status);
|
||||
}
|
||||
|
||||
inline void VideoProperty::SetString(std::string_view value) {
|
||||
m_status = 0;
|
||||
SetStringProperty(m_handle, value, &m_status);
|
||||
}
|
||||
|
||||
inline std::vector<std::string> VideoProperty::GetChoices() const {
|
||||
m_status = 0;
|
||||
return GetEnumPropertyChoices(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline VideoProperty::VideoProperty(CS_Property handle) : m_handle(handle) {
|
||||
m_status = 0;
|
||||
if (handle == 0) {
|
||||
m_kind = kNone;
|
||||
} else {
|
||||
m_kind =
|
||||
static_cast<Kind>(static_cast<int>(GetPropertyKind(handle, &m_status)));
|
||||
}
|
||||
}
|
||||
|
||||
inline VideoProperty::VideoProperty(CS_Property handle, Kind kind)
|
||||
: m_handle(handle), m_kind(kind) {}
|
||||
|
||||
inline VideoSource::VideoSource(const VideoSource& source)
|
||||
: m_handle(source.m_handle == 0 ? 0
|
||||
: CopySource(source.m_handle, &m_status)) {}
|
||||
|
||||
inline VideoSource::VideoSource(VideoSource&& other) noexcept : VideoSource() {
|
||||
swap(*this, other);
|
||||
}
|
||||
|
||||
inline VideoSource& VideoSource::operator=(VideoSource other) noexcept {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline VideoSource::~VideoSource() {
|
||||
m_status = 0;
|
||||
if (m_handle != 0) {
|
||||
ReleaseSource(m_handle, &m_status);
|
||||
}
|
||||
}
|
||||
|
||||
inline VideoSource::Kind VideoSource::GetKind() const {
|
||||
m_status = 0;
|
||||
return static_cast<VideoSource::Kind>(GetSourceKind(m_handle, &m_status));
|
||||
}
|
||||
|
||||
inline std::string VideoSource::GetName() const {
|
||||
m_status = 0;
|
||||
return GetSourceName(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline std::string VideoSource::GetDescription() const {
|
||||
m_status = 0;
|
||||
return GetSourceDescription(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline uint64_t VideoSource::GetLastFrameTime() const {
|
||||
m_status = 0;
|
||||
return GetSourceLastFrameTime(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline void VideoSource::SetConnectionStrategy(ConnectionStrategy strategy) {
|
||||
m_status = 0;
|
||||
SetSourceConnectionStrategy(
|
||||
m_handle, static_cast<CS_ConnectionStrategy>(static_cast<int>(strategy)),
|
||||
&m_status);
|
||||
}
|
||||
|
||||
inline bool VideoSource::IsConnected() const {
|
||||
m_status = 0;
|
||||
return IsSourceConnected(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline bool VideoSource::IsEnabled() const {
|
||||
m_status = 0;
|
||||
return IsSourceEnabled(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline VideoProperty VideoSource::GetProperty(std::string_view name) {
|
||||
m_status = 0;
|
||||
return VideoProperty{GetSourceProperty(m_handle, name, &m_status)};
|
||||
}
|
||||
|
||||
inline VideoMode VideoSource::GetVideoMode() const {
|
||||
m_status = 0;
|
||||
return GetSourceVideoMode(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline bool VideoSource::SetVideoMode(const VideoMode& mode) {
|
||||
m_status = 0;
|
||||
return SetSourceVideoMode(m_handle, mode, &m_status);
|
||||
}
|
||||
|
||||
inline bool VideoSource::SetVideoMode(VideoMode::PixelFormat pixelFormat,
|
||||
int width, int height, int fps) {
|
||||
m_status = 0;
|
||||
return SetSourceVideoMode(
|
||||
m_handle, VideoMode{pixelFormat, width, height, fps}, &m_status);
|
||||
}
|
||||
|
||||
inline bool VideoSource::SetPixelFormat(VideoMode::PixelFormat pixelFormat) {
|
||||
m_status = 0;
|
||||
return SetSourcePixelFormat(m_handle, pixelFormat, &m_status);
|
||||
}
|
||||
|
||||
inline bool VideoSource::SetResolution(int width, int height) {
|
||||
m_status = 0;
|
||||
return SetSourceResolution(m_handle, width, height, &m_status);
|
||||
}
|
||||
|
||||
inline bool VideoSource::SetFPS(int fps) {
|
||||
m_status = 0;
|
||||
return SetSourceFPS(m_handle, fps, &m_status);
|
||||
}
|
||||
|
||||
inline bool VideoSource::SetConfigJson(std::string_view config) {
|
||||
m_status = 0;
|
||||
return SetSourceConfigJson(m_handle, config, &m_status);
|
||||
}
|
||||
|
||||
inline bool VideoSource::SetConfigJson(const wpi::json& config) {
|
||||
m_status = 0;
|
||||
return SetSourceConfigJson(m_handle, config, &m_status);
|
||||
}
|
||||
|
||||
inline std::string VideoSource::GetConfigJson() const {
|
||||
m_status = 0;
|
||||
return GetSourceConfigJson(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline double VideoSource::GetActualFPS() const {
|
||||
m_status = 0;
|
||||
return cs::GetTelemetryAverageValue(m_handle, CS_SOURCE_FRAMES_RECEIVED,
|
||||
&m_status);
|
||||
}
|
||||
|
||||
inline double VideoSource::GetActualDataRate() const {
|
||||
m_status = 0;
|
||||
return cs::GetTelemetryAverageValue(m_handle, CS_SOURCE_BYTES_RECEIVED,
|
||||
&m_status);
|
||||
}
|
||||
|
||||
inline std::vector<VideoMode> VideoSource::EnumerateVideoModes() const {
|
||||
CS_Status status = 0;
|
||||
return EnumerateSourceVideoModes(m_handle, &status);
|
||||
}
|
||||
|
||||
inline void VideoCamera::SetBrightness(int brightness) {
|
||||
m_status = 0;
|
||||
SetCameraBrightness(m_handle, brightness, &m_status);
|
||||
}
|
||||
|
||||
inline int VideoCamera::GetBrightness() {
|
||||
m_status = 0;
|
||||
return GetCameraBrightness(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline void VideoCamera::SetWhiteBalanceAuto() {
|
||||
m_status = 0;
|
||||
SetCameraWhiteBalanceAuto(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline void VideoCamera::SetWhiteBalanceHoldCurrent() {
|
||||
m_status = 0;
|
||||
SetCameraWhiteBalanceHoldCurrent(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline void VideoCamera::SetWhiteBalanceManual(int value) {
|
||||
m_status = 0;
|
||||
SetCameraWhiteBalanceManual(m_handle, value, &m_status);
|
||||
}
|
||||
|
||||
inline void VideoCamera::SetExposureAuto() {
|
||||
m_status = 0;
|
||||
SetCameraExposureAuto(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline void VideoCamera::SetExposureHoldCurrent() {
|
||||
m_status = 0;
|
||||
SetCameraExposureHoldCurrent(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline void VideoCamera::SetExposureManual(int value) {
|
||||
m_status = 0;
|
||||
SetCameraExposureManual(m_handle, value, &m_status);
|
||||
}
|
||||
|
||||
inline UsbCamera::UsbCamera(std::string_view name, int dev) {
|
||||
m_handle = CreateUsbCameraDev(name, dev, &m_status);
|
||||
}
|
||||
|
||||
inline UsbCamera::UsbCamera(std::string_view name, std::string_view path) {
|
||||
m_handle = CreateUsbCameraPath(name, path, &m_status);
|
||||
}
|
||||
|
||||
inline std::vector<UsbCameraInfo> UsbCamera::EnumerateUsbCameras() {
|
||||
CS_Status status = 0;
|
||||
return ::cs::EnumerateUsbCameras(&status);
|
||||
}
|
||||
|
||||
inline void UsbCamera::SetPath(std::string_view path) {
|
||||
m_status = 0;
|
||||
return ::cs::SetUsbCameraPath(m_handle, path, &m_status);
|
||||
}
|
||||
|
||||
inline std::string UsbCamera::GetPath() const {
|
||||
m_status = 0;
|
||||
return ::cs::GetUsbCameraPath(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline UsbCameraInfo UsbCamera::GetInfo() const {
|
||||
m_status = 0;
|
||||
return ::cs::GetUsbCameraInfo(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline void UsbCamera::SetConnectVerbose(int level) {
|
||||
m_status = 0;
|
||||
SetProperty(GetSourceProperty(m_handle, "connect_verbose", &m_status), level,
|
||||
&m_status);
|
||||
}
|
||||
|
||||
inline HttpCamera::HttpCamera(std::string_view name, std::string_view url,
|
||||
HttpCameraKind kind) {
|
||||
m_handle = CreateHttpCamera(
|
||||
name, url, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
|
||||
&m_status);
|
||||
}
|
||||
|
||||
inline HttpCamera::HttpCamera(std::string_view name, const char* url,
|
||||
HttpCameraKind kind) {
|
||||
m_handle = CreateHttpCamera(
|
||||
name, url, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
|
||||
&m_status);
|
||||
}
|
||||
|
||||
inline HttpCamera::HttpCamera(std::string_view name, const std::string& url,
|
||||
HttpCameraKind kind)
|
||||
: HttpCamera(name, std::string_view{url}, kind) {}
|
||||
|
||||
inline HttpCamera::HttpCamera(std::string_view name,
|
||||
std::span<const std::string> urls,
|
||||
HttpCameraKind kind) {
|
||||
m_handle = CreateHttpCamera(
|
||||
name, urls, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
|
||||
&m_status);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline HttpCamera::HttpCamera(std::string_view name,
|
||||
std::initializer_list<T> urls,
|
||||
HttpCameraKind kind) {
|
||||
std::vector<std::string> vec;
|
||||
vec.reserve(urls.size());
|
||||
for (const auto& url : urls) {
|
||||
vec.emplace_back(url);
|
||||
}
|
||||
m_handle = CreateHttpCamera(
|
||||
name, vec, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
|
||||
&m_status);
|
||||
}
|
||||
|
||||
inline HttpCamera::HttpCameraKind HttpCamera::GetHttpCameraKind() const {
|
||||
m_status = 0;
|
||||
return static_cast<HttpCameraKind>(
|
||||
static_cast<int>(::cs::GetHttpCameraKind(m_handle, &m_status)));
|
||||
}
|
||||
|
||||
inline void HttpCamera::SetUrls(std::span<const std::string> urls) {
|
||||
m_status = 0;
|
||||
::cs::SetHttpCameraUrls(m_handle, urls, &m_status);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void HttpCamera::SetUrls(std::initializer_list<T> urls) {
|
||||
std::vector<std::string> vec;
|
||||
vec.reserve(urls.size());
|
||||
for (const auto& url : urls) {
|
||||
vec.emplace_back(url);
|
||||
}
|
||||
m_status = 0;
|
||||
::cs::SetHttpCameraUrls(m_handle, vec, &m_status);
|
||||
}
|
||||
|
||||
inline std::vector<std::string> HttpCamera::GetUrls() const {
|
||||
m_status = 0;
|
||||
return ::cs::GetHttpCameraUrls(m_handle, &m_status);
|
||||
}
|
||||
WPI_IGNORE_DEPRECATED
|
||||
inline std::vector<std::string> AxisCamera::HostToUrl(
|
||||
std::span<const std::string> hosts) {
|
||||
std::vector<std::string> rv;
|
||||
rv.reserve(hosts.size());
|
||||
for (const auto& host : hosts) {
|
||||
rv.emplace_back(HostToUrl(std::string_view{host}));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::vector<std::string> AxisCamera::HostToUrl(
|
||||
std::initializer_list<T> hosts) {
|
||||
std::vector<std::string> rv;
|
||||
rv.reserve(hosts.size());
|
||||
for (const auto& host : hosts) {
|
||||
rv.emplace_back(HostToUrl(std::string_view{host}));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
inline AxisCamera::AxisCamera(std::string_view name, std::string_view host)
|
||||
: HttpCamera(name, HostToUrl(host), kAxis) {}
|
||||
|
||||
inline AxisCamera::AxisCamera(std::string_view name, const char* host)
|
||||
: HttpCamera(name, HostToUrl(host), kAxis) {}
|
||||
|
||||
inline AxisCamera::AxisCamera(std::string_view name, const std::string& host)
|
||||
: HttpCamera(name, HostToUrl(std::string_view{host}), kAxis) {}
|
||||
|
||||
inline AxisCamera::AxisCamera(std::string_view name,
|
||||
std::span<const std::string> hosts)
|
||||
: HttpCamera(name, HostToUrl(hosts), kAxis) {}
|
||||
|
||||
template <typename T>
|
||||
inline AxisCamera::AxisCamera(std::string_view name,
|
||||
std::initializer_list<T> hosts)
|
||||
: HttpCamera(name, HostToUrl(hosts), kAxis) {}
|
||||
WPI_UNIGNORE_DEPRECATED
|
||||
|
||||
inline void ImageSource::NotifyError(std::string_view msg) {
|
||||
m_status = 0;
|
||||
NotifySourceError(m_handle, msg, &m_status);
|
||||
}
|
||||
|
||||
inline void ImageSource::SetConnected(bool connected) {
|
||||
m_status = 0;
|
||||
SetSourceConnected(m_handle, connected, &m_status);
|
||||
}
|
||||
|
||||
inline void ImageSource::SetDescription(std::string_view description) {
|
||||
m_status = 0;
|
||||
SetSourceDescription(m_handle, description, &m_status);
|
||||
}
|
||||
|
||||
inline VideoProperty ImageSource::CreateProperty(std::string_view name,
|
||||
VideoProperty::Kind kind,
|
||||
int minimum, int maximum,
|
||||
int step, int defaultValue,
|
||||
int value) {
|
||||
m_status = 0;
|
||||
return VideoProperty{CreateSourceProperty(
|
||||
m_handle, name, static_cast<CS_PropertyKind>(static_cast<int>(kind)),
|
||||
minimum, maximum, step, defaultValue, value, &m_status)};
|
||||
}
|
||||
|
||||
inline VideoProperty ImageSource::CreateIntegerProperty(std::string_view name,
|
||||
int minimum,
|
||||
int maximum, int step,
|
||||
int defaultValue,
|
||||
int value) {
|
||||
m_status = 0;
|
||||
return VideoProperty{CreateSourceProperty(
|
||||
m_handle, name,
|
||||
static_cast<CS_PropertyKind>(
|
||||
static_cast<int>(VideoProperty::Kind::kInteger)),
|
||||
minimum, maximum, step, defaultValue, value, &m_status)};
|
||||
}
|
||||
|
||||
inline VideoProperty ImageSource::CreateBooleanProperty(std::string_view name,
|
||||
bool defaultValue,
|
||||
bool value) {
|
||||
m_status = 0;
|
||||
return VideoProperty{CreateSourceProperty(
|
||||
m_handle, name,
|
||||
static_cast<CS_PropertyKind>(
|
||||
static_cast<int>(VideoProperty::Kind::kBoolean)),
|
||||
0, 1, 1, defaultValue ? 1 : 0, value ? 1 : 0, &m_status)};
|
||||
}
|
||||
|
||||
inline VideoProperty ImageSource::CreateStringProperty(std::string_view name,
|
||||
std::string_view value) {
|
||||
m_status = 0;
|
||||
auto prop = VideoProperty{
|
||||
CreateSourceProperty(m_handle, name,
|
||||
static_cast<CS_PropertyKind>(
|
||||
static_cast<int>(VideoProperty::Kind::kString)),
|
||||
0, 0, 0, 0, 0, &m_status)};
|
||||
prop.SetString(value);
|
||||
return prop;
|
||||
}
|
||||
|
||||
inline void ImageSource::SetEnumPropertyChoices(
|
||||
const VideoProperty& property, std::span<const std::string> choices) {
|
||||
m_status = 0;
|
||||
SetSourceEnumPropertyChoices(m_handle, property.m_handle, choices, &m_status);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void ImageSource::SetEnumPropertyChoices(
|
||||
const VideoProperty& property, std::initializer_list<T> choices) {
|
||||
std::vector<std::string> vec;
|
||||
vec.reserve(choices.size());
|
||||
for (const auto& choice : choices) {
|
||||
vec.emplace_back(choice);
|
||||
}
|
||||
m_status = 0;
|
||||
SetSourceEnumPropertyChoices(m_handle, property.m_handle, vec, &m_status);
|
||||
}
|
||||
|
||||
inline VideoSink::VideoSink(const VideoSink& sink)
|
||||
: m_handle(sink.m_handle == 0 ? 0 : CopySink(sink.m_handle, &m_status)) {}
|
||||
|
||||
inline VideoSink::VideoSink(VideoSink&& other) noexcept : VideoSink() {
|
||||
swap(*this, other);
|
||||
}
|
||||
|
||||
inline VideoSink& VideoSink::operator=(VideoSink other) noexcept {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline VideoSink::~VideoSink() {
|
||||
m_status = 0;
|
||||
if (m_handle != 0) {
|
||||
ReleaseSink(m_handle, &m_status);
|
||||
}
|
||||
}
|
||||
|
||||
inline VideoSink::Kind VideoSink::GetKind() const {
|
||||
m_status = 0;
|
||||
return static_cast<VideoSink::Kind>(GetSinkKind(m_handle, &m_status));
|
||||
}
|
||||
|
||||
inline std::string VideoSink::GetName() const {
|
||||
m_status = 0;
|
||||
return GetSinkName(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline std::string VideoSink::GetDescription() const {
|
||||
m_status = 0;
|
||||
return GetSinkDescription(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline VideoProperty VideoSink::GetProperty(std::string_view name) {
|
||||
m_status = 0;
|
||||
return VideoProperty{GetSinkProperty(m_handle, name, &m_status)};
|
||||
}
|
||||
|
||||
inline void VideoSink::SetSource(VideoSource source) {
|
||||
m_status = 0;
|
||||
if (!source) {
|
||||
SetSinkSource(m_handle, 0, &m_status);
|
||||
} else {
|
||||
SetSinkSource(m_handle, source.m_handle, &m_status);
|
||||
}
|
||||
}
|
||||
|
||||
inline VideoSource VideoSink::GetSource() const {
|
||||
m_status = 0;
|
||||
auto handle = GetSinkSource(m_handle, &m_status);
|
||||
return VideoSource{handle == 0 ? 0 : CopySource(handle, &m_status)};
|
||||
}
|
||||
|
||||
inline VideoProperty VideoSink::GetSourceProperty(std::string_view name) {
|
||||
m_status = 0;
|
||||
return VideoProperty{GetSinkSourceProperty(m_handle, name, &m_status)};
|
||||
}
|
||||
|
||||
inline bool VideoSink::SetConfigJson(std::string_view config) {
|
||||
m_status = 0;
|
||||
return SetSinkConfigJson(m_handle, config, &m_status);
|
||||
}
|
||||
|
||||
inline bool VideoSink::SetConfigJson(const wpi::json& config) {
|
||||
m_status = 0;
|
||||
return SetSinkConfigJson(m_handle, config, &m_status);
|
||||
}
|
||||
|
||||
inline std::string VideoSink::GetConfigJson() const {
|
||||
m_status = 0;
|
||||
return GetSinkConfigJson(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline MjpegServer::MjpegServer(std::string_view name,
|
||||
std::string_view listenAddress, int port) {
|
||||
m_handle = CreateMjpegServer(name, listenAddress, port, &m_status);
|
||||
}
|
||||
|
||||
inline std::string MjpegServer::GetListenAddress() const {
|
||||
m_status = 0;
|
||||
return cs::GetMjpegServerListenAddress(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline int MjpegServer::GetPort() const {
|
||||
m_status = 0;
|
||||
return cs::GetMjpegServerPort(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline void MjpegServer::SetResolution(int width, int height) {
|
||||
m_status = 0;
|
||||
SetProperty(GetSinkProperty(m_handle, "width", &m_status), width, &m_status);
|
||||
SetProperty(GetSinkProperty(m_handle, "height", &m_status), height,
|
||||
&m_status);
|
||||
}
|
||||
|
||||
inline void MjpegServer::SetFPS(int fps) {
|
||||
m_status = 0;
|
||||
SetProperty(GetSinkProperty(m_handle, "fps", &m_status), fps, &m_status);
|
||||
}
|
||||
|
||||
inline void MjpegServer::SetCompression(int quality) {
|
||||
m_status = 0;
|
||||
SetProperty(GetSinkProperty(m_handle, "compression", &m_status), quality,
|
||||
&m_status);
|
||||
}
|
||||
|
||||
inline void MjpegServer::SetDefaultCompression(int quality) {
|
||||
m_status = 0;
|
||||
SetProperty(GetSinkProperty(m_handle, "default_compression", &m_status),
|
||||
quality, &m_status);
|
||||
}
|
||||
|
||||
inline void ImageSink::SetDescription(std::string_view description) {
|
||||
m_status = 0;
|
||||
SetSinkDescription(m_handle, description, &m_status);
|
||||
}
|
||||
|
||||
inline std::string ImageSink::GetError() const {
|
||||
m_status = 0;
|
||||
return GetSinkError(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline void ImageSink::SetEnabled(bool enabled) {
|
||||
m_status = 0;
|
||||
SetSinkEnabled(m_handle, enabled, &m_status);
|
||||
}
|
||||
|
||||
inline VideoSource VideoEvent::GetSource() const {
|
||||
CS_Status status = 0;
|
||||
return VideoSource{sourceHandle == 0 ? 0 : CopySource(sourceHandle, &status)};
|
||||
}
|
||||
|
||||
inline VideoSink VideoEvent::GetSink() const {
|
||||
CS_Status status = 0;
|
||||
return VideoSink{sinkHandle == 0 ? 0 : CopySink(sinkHandle, &status)};
|
||||
}
|
||||
|
||||
inline VideoProperty VideoEvent::GetProperty() const {
|
||||
return VideoProperty{propertyHandle,
|
||||
static_cast<VideoProperty::Kind>(propertyKind)};
|
||||
}
|
||||
|
||||
inline VideoListener::VideoListener(
|
||||
std::function<void(const VideoEvent& event)> callback, int eventMask,
|
||||
bool immediateNotify) {
|
||||
CS_Status status = 0;
|
||||
m_handle = AddListener(
|
||||
[=](const RawEvent& event) {
|
||||
callback(static_cast<const VideoEvent&>(event));
|
||||
},
|
||||
eventMask, immediateNotify, &status);
|
||||
}
|
||||
|
||||
inline VideoListener::VideoListener(VideoListener&& other) noexcept
|
||||
: VideoListener() {
|
||||
swap(*this, other);
|
||||
}
|
||||
|
||||
inline VideoListener& VideoListener::operator=(VideoListener&& other) noexcept {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline VideoListener::~VideoListener() {
|
||||
CS_Status status = 0;
|
||||
if (m_handle != 0) {
|
||||
RemoveListener(m_handle, &status);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
#endif // CSCORE_CSCORE_OO_INC_
|
||||
@@ -75,25 +75,6 @@ size_t File::Read(void* buf, uint32_t count) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
File::AsyncId File::AsyncReadBegin(uint32_t len) const {
|
||||
int rv = sftp_async_read_begin(m_handle, len);
|
||||
if (rv < 0) {
|
||||
throw Exception{m_handle->sftp};
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
size_t File::AsyncRead(void* data, uint32_t len, AsyncId id) {
|
||||
auto rv = sftp_async_read(m_handle, data, len, id);
|
||||
if (rv == SSH_ERROR) {
|
||||
throw Exception{ssh_get_error(m_handle->sftp->session)};
|
||||
}
|
||||
if (rv == SSH_AGAIN) {
|
||||
return 0;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
size_t File::Write(std::span<const uint8_t> data) {
|
||||
auto rv = sftp_write(m_handle, data.data(), data.size());
|
||||
if (rv < 0) {
|
||||
|
||||
@@ -47,11 +47,7 @@ class File {
|
||||
void SetNonblocking() { sftp_file_set_nonblocking(m_handle); }
|
||||
void SetBlocking() { sftp_file_set_blocking(m_handle); }
|
||||
|
||||
using AsyncId = uint32_t;
|
||||
|
||||
size_t Read(void* buf, uint32_t count);
|
||||
AsyncId AsyncReadBegin(uint32_t len) const;
|
||||
size_t AsyncRead(void* data, uint32_t len, AsyncId id);
|
||||
size_t Write(std::span<const uint8_t> data);
|
||||
|
||||
void Seek(uint64_t offset);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id "org.ysb33r.doxygen" version "1.0.3"
|
||||
id "org.ysb33r.doxygen" version "1.0.4"
|
||||
}
|
||||
|
||||
evaluationDependsOn(':apriltag')
|
||||
|
||||
@@ -95,7 +95,10 @@ public class AnnotationProcessor extends AbstractProcessor {
|
||||
// in this list will determine how it gets logged.
|
||||
m_handlers =
|
||||
List.of(
|
||||
new LoggableHandler(processingEnv), // prioritize epilogue logging over Sendable
|
||||
new LoggableHandler(
|
||||
processingEnv,
|
||||
roundEnv.getElementsAnnotatedWith(
|
||||
Logged.class)), // prioritize epilogue logging over Sendable
|
||||
new ConfiguredLoggerHandler(
|
||||
processingEnv, customLoggers), // then customized logging configs
|
||||
new ArrayHandler(processingEnv),
|
||||
|
||||
@@ -61,15 +61,46 @@ public abstract class ElementHandler {
|
||||
* @return the name specified in the {@link Logged @Logged} annotation on the element, if present;
|
||||
* otherwise, the field or method's name with no modifications
|
||||
*/
|
||||
public String loggedName(Element element) {
|
||||
var elementName = element.getSimpleName().toString();
|
||||
var config = element.getAnnotation(Logged.class);
|
||||
public static String loggedName(Element element) {
|
||||
var elementConfig = element.getAnnotation(Logged.class);
|
||||
|
||||
if (config != null && !config.name().isBlank()) {
|
||||
return config.name();
|
||||
} else {
|
||||
return elementName;
|
||||
// Use the name provided on the logged element, if one is present
|
||||
if (elementConfig != null && !elementConfig.name().isBlank()) {
|
||||
return elementConfig.name();
|
||||
}
|
||||
|
||||
var config = elementConfig;
|
||||
|
||||
if (config == null) {
|
||||
// Look up the parent class configuration
|
||||
// We assume one is present, since logged elements should only be found if the enclosing class
|
||||
// is @Logged itself
|
||||
Logged parentConfig = null;
|
||||
for (var parent = element.getEnclosingElement();
|
||||
parent != null;
|
||||
parent = parent.getEnclosingElement()) {
|
||||
parentConfig = parent.getAnnotation(Logged.class);
|
||||
if (parentConfig != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
config = parentConfig;
|
||||
}
|
||||
|
||||
if (config == null) {
|
||||
// Uh oh
|
||||
throw new IllegalStateException(
|
||||
"Could not generate a name for element "
|
||||
+ element
|
||||
+ " without a @Logged annotation AND without being contained within a class with a @Logged annotation!\n\nOpen an issue at https://github.com/wpilibsuite/allwpilib/issues and include a copy of the file that caused this error.");
|
||||
}
|
||||
|
||||
var elementName = element.getSimpleName().toString();
|
||||
return switch (config.defaultNaming()) {
|
||||
case USE_CODE_NAME -> elementName;
|
||||
case USE_HUMAN_NAME -> StringUtils.toHumanName(elementName);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -176,7 +176,7 @@ public class EpilogueGenerator {
|
||||
out.println(" config.loggingPeriod = Seconds.of(robot.getPeriod());");
|
||||
out.println(" }");
|
||||
out.println(" if (config.loggingPeriodOffset == null) {");
|
||||
out.println(" config.loggingPeriodOffset = config.loggingPeriod.divide(2);");
|
||||
out.println(" config.loggingPeriodOffset = config.loggingPeriod.div(2);");
|
||||
out.println(" }");
|
||||
out.println();
|
||||
out.println(" robot.addPeriodic(() -> {");
|
||||
|
||||
@@ -5,15 +5,32 @@
|
||||
package edu.wpi.first.epilogue.processor;
|
||||
|
||||
import edu.wpi.first.epilogue.Logged;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
/** Handles logging for types annotated with the {@link Logged @Logged} annotation. */
|
||||
public class LoggableHandler extends ElementHandler {
|
||||
protected LoggableHandler(ProcessingEnvironment processingEnv) {
|
||||
private final Set<TypeElement> m_loggedTypes;
|
||||
|
||||
protected LoggableHandler(
|
||||
ProcessingEnvironment processingEnv, Collection<? extends Element> loggedTypes) {
|
||||
super(processingEnv);
|
||||
m_loggedTypes =
|
||||
loggedTypes.stream()
|
||||
.filter(e -> e instanceof TypeElement)
|
||||
.map(e -> (TypeElement) e)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -27,17 +44,165 @@ public class LoggableHandler extends ElementHandler {
|
||||
@Override
|
||||
public String logInvocation(Element element) {
|
||||
TypeMirror dataType = dataType(element);
|
||||
var reflectedType =
|
||||
var declaredType =
|
||||
m_processingEnv
|
||||
.getElementUtils()
|
||||
.getTypeElement(m_processingEnv.getTypeUtils().erasure(dataType).toString());
|
||||
|
||||
return "Epilogue."
|
||||
+ StringUtils.loggerFieldName(reflectedType)
|
||||
+ ".tryUpdate(dataLogger.getSubLogger(\""
|
||||
+ loggedName(element)
|
||||
+ "\"), "
|
||||
+ elementAccess(element)
|
||||
+ ", Epilogue.getConfig().errorHandler)";
|
||||
// Get the list of known loggable subtypes of the input type. This will include the input type.
|
||||
// These are sorted by their distance from the declared type such that "more concrete" types are
|
||||
// checked first so the instanceof chain doesn't check a really generic type first, even if a
|
||||
// more specific loggable type could be used instead.
|
||||
var loggableSubtypes =
|
||||
m_loggedTypes.stream()
|
||||
.filter(
|
||||
l -> m_processingEnv.getTypeUtils().isAssignable(l.asType(), declaredType.asType()))
|
||||
.sorted(inheritanceComparatorFor(declaredType))
|
||||
.toList();
|
||||
|
||||
int size = loggableSubtypes.size();
|
||||
|
||||
// If there are no known loggable subtypes, return just the single logger call
|
||||
if (size == 1) {
|
||||
return generateLoggerCall(element, declaredType, elementAccess(element));
|
||||
}
|
||||
|
||||
// Otherwise, generate an if-else chain to compare the element with its known loggable subtypes
|
||||
// and implementations. A subclass without a @Logged annotation will be logged at runtime using
|
||||
// the generic logger for whatever the field or method's declared type is.
|
||||
|
||||
String varName = cacheVariableName(element);
|
||||
|
||||
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)));
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
TypeElement type = loggableSubtypes.get(i);
|
||||
String part;
|
||||
|
||||
if (i == 0) {
|
||||
// First invocation, generate an "if" statement
|
||||
part = generateIf(type, element, "if", varName);
|
||||
} else if (i == size - 1) {
|
||||
// Final invocation, generate an "else" statement
|
||||
String loggerCall = generateLoggerCall(element, type, varName);
|
||||
part =
|
||||
" else {\n // Base type %s\n %s;\n}"
|
||||
.formatted(declaredType.getQualifiedName(), loggerCall);
|
||||
} else {
|
||||
// Somewhere in the middle, generate an "else if" statement
|
||||
part = generateIf(type, element, " else if", varName);
|
||||
}
|
||||
|
||||
builder.append(part);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the name of the cache variable to use for a logged element.
|
||||
*
|
||||
* @param element the logged element
|
||||
* @return the cache variable name
|
||||
*/
|
||||
private static String cacheVariableName(Element element) {
|
||||
// Generate unique names in case a field and a method share the same name
|
||||
if (element instanceof VariableElement) {
|
||||
return "$$%s".formatted(element.getSimpleName().toString());
|
||||
} else if (element instanceof ExecutableElement) {
|
||||
return "__%s".formatted(element.getSimpleName().toString());
|
||||
} else {
|
||||
// Generic fallback (shouldn't get here, since only fields and methods are logged)
|
||||
return element.getSimpleName().toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a comparator for sorting inheritors of a given type by their distance (farthest first)
|
||||
* for use in generating if-else instanceof chains. Inheritors at the same distance from the base
|
||||
* type will be further compared so classes come before interfaces, any any further ties are
|
||||
* broken alphabetically by fully-qualified type names.
|
||||
*
|
||||
* @param declaredType the declared type
|
||||
* @return the comparator
|
||||
*/
|
||||
private Comparator<TypeElement> inheritanceComparatorFor(TypeElement declaredType) {
|
||||
Comparator<TypeElement> byDistance =
|
||||
Comparator.comparingInt(
|
||||
inheritor -> {
|
||||
return inheritanceDistance(inheritor.asType(), declaredType.asType());
|
||||
});
|
||||
|
||||
return byDistance
|
||||
.reversed()
|
||||
.thenComparing(type -> type.getKind() == ElementKind.INTERFACE ? 1 : 0)
|
||||
.thenComparing(type -> type.getQualifiedName().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an instanceof if or if-else statement that checks the type and logs the element using
|
||||
* the logger for the given type, if they're compatible.
|
||||
*
|
||||
* @param type the type to generate the check for
|
||||
* @param element the element to be logged
|
||||
* @param keyword either "if" or " else if"
|
||||
* @param varName the name of the variable in the "instanceof" check
|
||||
* @return the if or else-if statement
|
||||
*/
|
||||
private String generateIf(TypeElement type, Element element, String keyword, String varName) {
|
||||
String ref = type.getQualifiedName().toString().replace('.', '_');
|
||||
String loggerCall = generateLoggerCall(element, type, ref);
|
||||
|
||||
return "%s (%s instanceof %s %s) {\n %s;\n}"
|
||||
.formatted(keyword, varName, type.getQualifiedName(), ref, loggerCall);
|
||||
}
|
||||
|
||||
private String generateLoggerCall(Element element, TypeElement type, String elementReference) {
|
||||
return ("Epilogue.%s.tryUpdate(dataLogger.getSubLogger(\"%s\"), %s, "
|
||||
+ "Epilogue.getConfig().errorHandler)")
|
||||
.formatted(StringUtils.loggerFieldName(type), loggedName(element), elementReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the minimum inheritance distance between two types; that is, how many "extends" or
|
||||
* "implements" clauses are required to get from one to the other.
|
||||
*
|
||||
* @param toCheck the type to check
|
||||
* @param base the base type to check against
|
||||
* @return the inheritance distance
|
||||
*/
|
||||
private int inheritanceDistance(TypeMirror toCheck, TypeMirror base) {
|
||||
var types = m_processingEnv.getTypeUtils();
|
||||
|
||||
if (types.isSameType(toCheck, base)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int distance = 1;
|
||||
var parent = toCheck;
|
||||
TypeElement element = m_processingEnv.getElementUtils().getTypeElement(parent.toString());
|
||||
while (!types.isSameType(parent, base)
|
||||
&& element.getInterfaces().stream().noneMatch(i -> types.isSameType(i, base))) {
|
||||
element = m_processingEnv.getElementUtils().getTypeElement(parent.toString());
|
||||
if (parent.getKind() == TypeKind.NONE) {
|
||||
// Interface inheritance, there is no superclass
|
||||
break;
|
||||
}
|
||||
|
||||
parent = element.getSuperclass();
|
||||
|
||||
// Handle cases of interface inheritance
|
||||
distance =
|
||||
1
|
||||
+ element.getInterfaces().stream()
|
||||
.mapToInt(iface -> inheritanceDistance(iface, base))
|
||||
.min()
|
||||
.orElse(distance);
|
||||
}
|
||||
|
||||
return distance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,23 +7,36 @@ package edu.wpi.first.epilogue.processor;
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
import com.sun.source.tree.ReturnTree;
|
||||
import com.sun.source.util.SimpleTreeVisitor;
|
||||
import com.sun.source.util.Trees;
|
||||
import edu.wpi.first.epilogue.Logged;
|
||||
import edu.wpi.first.epilogue.NotLogged;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.Name;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
/** Generates logger class files for {@link Logged @Logged}-annotated classes. */
|
||||
public class LoggerGenerator {
|
||||
public static final Predicate<ExecutableElement> kIsBuiltInJavaMethod =
|
||||
LoggerGenerator::isBuiltInJavaMethod;
|
||||
private final ProcessingEnvironment m_processingEnv;
|
||||
private final List<ElementHandler> m_handlers;
|
||||
|
||||
@@ -36,6 +49,19 @@ public class LoggerGenerator {
|
||||
return e.getAnnotation(NotLogged.class) == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a method is a method declared in java.lang.Object that should not be logged.
|
||||
*
|
||||
* @param e the method to check
|
||||
* @return true if the method is toString, hashCode, or clone; false otherwise
|
||||
*/
|
||||
private static boolean isBuiltInJavaMethod(ExecutableElement e) {
|
||||
Name name = e.getSimpleName();
|
||||
return name.contentEquals("toString")
|
||||
|| name.contentEquals("hashCode")
|
||||
|| name.contentEquals("clone");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the logger class used to handle data objects of the given type. The generated logger
|
||||
* class will subclass from {@link edu.wpi.first.epilogue.logging.ClassSpecificLogger} and
|
||||
@@ -53,17 +79,23 @@ public class LoggerGenerator {
|
||||
Predicate<Element> optedIn =
|
||||
e -> !requireExplicitOptIn || e.getAnnotation(Logged.class) != null;
|
||||
|
||||
var fieldsToLog =
|
||||
clazz.getEnclosedElements().stream()
|
||||
.filter(e -> e instanceof VariableElement)
|
||||
.map(e -> (VariableElement) e)
|
||||
.filter(notSkipped)
|
||||
.filter(optedIn)
|
||||
.filter(e -> !e.getModifiers().contains(Modifier.STATIC))
|
||||
.filter(this::isLoggable)
|
||||
.toList();
|
||||
List<VariableElement> fieldsToLog;
|
||||
if (Objects.equals(clazz.getSuperclass().toString(), "java.lang.Record")) {
|
||||
// Do not log record members - just use the accessor methods
|
||||
fieldsToLog = List.of();
|
||||
} else {
|
||||
fieldsToLog =
|
||||
clazz.getEnclosedElements().stream()
|
||||
.filter(e -> e instanceof VariableElement)
|
||||
.map(e -> (VariableElement) e)
|
||||
.filter(notSkipped)
|
||||
.filter(optedIn)
|
||||
.filter(e -> !e.getModifiers().contains(Modifier.STATIC))
|
||||
.filter(this::isLoggable)
|
||||
.toList();
|
||||
}
|
||||
|
||||
var methodsToLog =
|
||||
List<ExecutableElement> methodsToLog =
|
||||
clazz.getEnclosedElements().stream()
|
||||
.filter(e -> e instanceof ExecutableElement)
|
||||
.map(e -> (ExecutableElement) e)
|
||||
@@ -73,9 +105,51 @@ public class LoggerGenerator {
|
||||
.filter(e -> e.getModifiers().contains(Modifier.PUBLIC))
|
||||
.filter(e -> e.getParameters().isEmpty())
|
||||
.filter(e -> e.getReceiverType() != null)
|
||||
.filter(kIsBuiltInJavaMethod.negate())
|
||||
.filter(this::isLoggable)
|
||||
.filter(e -> !isSimpleGetterMethodForLoggedField(e, fieldsToLog))
|
||||
.toList();
|
||||
|
||||
// Validate no name collisions
|
||||
Map<String, List<Element>> usedNames =
|
||||
Stream.concat(fieldsToLog.stream(), methodsToLog.stream())
|
||||
.reduce(
|
||||
new HashMap<>(),
|
||||
(map, element) -> {
|
||||
String name = ElementHandler.loggedName(element);
|
||||
map.computeIfAbsent(name, _k -> new ArrayList<>()).add(element);
|
||||
|
||||
return map;
|
||||
},
|
||||
(left, right) -> {
|
||||
left.putAll(right);
|
||||
return left;
|
||||
});
|
||||
|
||||
usedNames.forEach(
|
||||
(name, elements) -> {
|
||||
if (elements.size() > 1) {
|
||||
// Collisions!
|
||||
for (Element conflictingElement : elements) {
|
||||
String conflicts =
|
||||
elements.stream()
|
||||
.filter(e -> !e.equals(conflictingElement))
|
||||
.map(e -> e.getEnclosingElement().getSimpleName() + "." + e)
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
m_processingEnv
|
||||
.getMessager()
|
||||
.printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
"[EPILOGUE] Conflicting name detected: \""
|
||||
+ name
|
||||
+ "\" is also used by "
|
||||
+ conflicts,
|
||||
conflictingElement);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
writeLoggerFile(clazz.getQualifiedName().toString(), config, fieldsToLog, methodsToLog);
|
||||
}
|
||||
|
||||
@@ -242,4 +316,55 @@ public class LoggerGenerator {
|
||||
private boolean isLoggable(Element element) {
|
||||
return m_handlers.stream().anyMatch(h -> h.isLoggable(element));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a method is a simple "getter" method for a field in the given list. Here, we define
|
||||
* "getter" as a method with a single return statement that references the name of a field, with
|
||||
* no other expressions. `getX() { return x; }` would be considered a "getter" method, while
|
||||
* `getX() { return x.clone(); }` would not be. Note that the method name is irrelevant; only the
|
||||
* method body is checked.
|
||||
*
|
||||
* @param ex the method to check
|
||||
* @param fieldsToLog the fields that will already be logged
|
||||
* @return true if the method is a simple "getter" method, false otherwise
|
||||
*/
|
||||
private boolean isSimpleGetterMethodForLoggedField(
|
||||
ExecutableElement ex, List<VariableElement> fieldsToLog) {
|
||||
var trees = Trees.instance(m_processingEnv);
|
||||
var methodTree = trees.getTree(ex);
|
||||
|
||||
if (methodTree == null) {
|
||||
// probably a record's synthetic reader method
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodTree.getBody() == null) {
|
||||
// Abstract or native method, can't be determined to be a getter
|
||||
return false;
|
||||
}
|
||||
|
||||
var statements = methodTree.getBody().getStatements();
|
||||
if (statements.size() != 1) {
|
||||
// More complex than a simple `return m_field` statement
|
||||
return false;
|
||||
}
|
||||
|
||||
var statement = statements.get(0);
|
||||
if (!(statement instanceof ReturnTree ret)) {
|
||||
// Shouldn't get here, since we've already filtered for methods that return a value
|
||||
// and with a single statement - that one statement should be the return
|
||||
return false;
|
||||
}
|
||||
|
||||
var returnExpression = ret.getExpression();
|
||||
return returnExpression.accept(
|
||||
new SimpleTreeVisitor<Boolean, Void>(false) {
|
||||
@Override
|
||||
public Boolean visitIdentifier(IdentifierTree identifier, Void unused) {
|
||||
return fieldsToLog.stream()
|
||||
.anyMatch(v -> v.getSimpleName().contentEquals(identifier.getName()));
|
||||
}
|
||||
},
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
package edu.wpi.first.epilogue.processor;
|
||||
|
||||
import edu.wpi.first.epilogue.Logged;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
|
||||
public final class StringUtils {
|
||||
@@ -59,6 +62,33 @@ public final class StringUtils {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a camel-cased string like "fooBar" into individual words like ["foo", "Bar"].
|
||||
*
|
||||
* @param camelCasedString the camel-cased string to split
|
||||
* @return the individual words in the input
|
||||
*/
|
||||
public static List<String> splitToWords(CharSequence camelCasedString) {
|
||||
// Implementation from https://stackoverflow.com/a/2560017, refactored for readability
|
||||
|
||||
// Uppercase letter not followed by the first letter of the next word
|
||||
// This allows for splitting "IOLayer" into "IO" and "Layer"
|
||||
String penultimateUppercaseLetter = "(?<=[A-Z])(?=[A-Z][a-z])";
|
||||
|
||||
// Any character that's NOT an uppercase letter, immediately followed by an uppercase letter
|
||||
// This allows for splitting "fooBar" into "foo" and "Bar", or "123Bang" into "123" and "Bang"
|
||||
String lastNonUppercaseLetter = "(?<=[^A-Z])(?=[A-Z])";
|
||||
|
||||
// The final letter in a sequence, followed by a non-alpha character like a number or underscore
|
||||
// This allows for splitting "foo123" into "foo" and "123"
|
||||
String finalLetter = "(?<=[A-Za-z])(?=[^A-Za-z])";
|
||||
|
||||
String regex =
|
||||
String.format("%s|%s|%s", penultimateUppercaseLetter, lastNonUppercaseLetter, finalLetter);
|
||||
|
||||
return Arrays.asList(camelCasedString.toString().split(regex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the field used to hold a logger for data of the given type.
|
||||
*
|
||||
@@ -107,4 +137,31 @@ public final class StringUtils {
|
||||
|
||||
return loggerClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a camelCase element name to separate words, removing common field and method name
|
||||
* prefixes like "m_" and "get".
|
||||
*
|
||||
* @param elementName the camelcased element name
|
||||
* @return the name split into separate words and sanitized
|
||||
*/
|
||||
public static String toHumanName(String elementName) {
|
||||
// Delete common field prefixes (k_name, m_name, s_name)
|
||||
var sanitizedName = elementName.replaceFirst("^[msk]_", "");
|
||||
|
||||
// Drop leading "k" prefix from fields
|
||||
// (though normally these should be static, and thus not logged)
|
||||
if (sanitizedName.matches("^k[A-Z].*$")) {
|
||||
sanitizedName = sanitizedName.substring(1);
|
||||
}
|
||||
|
||||
// Drop leading "get" from accessor methods
|
||||
if (sanitizedName.matches("^get[A-Z].*$")) {
|
||||
sanitizedName = sanitizedName.substring(3);
|
||||
}
|
||||
|
||||
return splitToWords(sanitizedName).stream()
|
||||
.map(StringUtils::capitalize)
|
||||
.collect(Collectors.joining(" "));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
|
||||
|
||||
import static com.google.testing.compile.CompilationSubject.assertThat;
|
||||
import static com.google.testing.compile.Compiler.javac;
|
||||
import static edu.wpi.first.epilogue.processor.CompileTestOptions.kJavaVersionOptions;
|
||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@@ -300,9 +301,9 @@ class AnnotationProcessorTest {
|
||||
byte[] arr1; // Should be logged
|
||||
byte[][] arr2; // Should not be logged
|
||||
|
||||
public byte getX() { return x; }
|
||||
public byte[] getArr1() { return arr1; }
|
||||
public byte[][] getArr2() { return arr2; }
|
||||
public byte getX() { return 0; }
|
||||
public byte[] getArr1() { return null; }
|
||||
public byte[][] getArr2() { return null; }
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -347,9 +348,9 @@ class AnnotationProcessorTest {
|
||||
char[] arr1; // Should not be logged
|
||||
char[][] arr2; // Should not be logged
|
||||
|
||||
public char getX() { return x; }
|
||||
public char[] getArr1() { return arr1; }
|
||||
public char[][] getArr2() { return arr2; }
|
||||
public char getX() { return 'x'; }
|
||||
public char[] getArr1() { return null; }
|
||||
public char[][] getArr2() { return null; }
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -392,9 +393,9 @@ class AnnotationProcessorTest {
|
||||
short[] arr1; // Should not be logged
|
||||
short[][] arr2; // Should not be logged
|
||||
|
||||
public short getX() { return x; }
|
||||
public short[] getArr1() { return arr1; }
|
||||
public short[][] getArr2() { return arr2; }
|
||||
public short getX() { return 0; }
|
||||
public short[] getArr1() { return null; }
|
||||
public short[][] getArr2() { return null; }
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -437,9 +438,9 @@ class AnnotationProcessorTest {
|
||||
int[] arr1; // Should be logged
|
||||
int[][] arr2; // Should not be logged
|
||||
|
||||
public int getX() { return x; }
|
||||
public int[] getArr1() { return arr1; }
|
||||
public int[][] getArr2() { return arr2; }
|
||||
public int getX() { return 0; }
|
||||
public int[] getArr1() { return null; }
|
||||
public int[][] getArr2() { return null; }
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -484,9 +485,9 @@ class AnnotationProcessorTest {
|
||||
long[] arr1; // Should be logged
|
||||
long[][] arr2; // Should not be logged
|
||||
|
||||
public long getX() { return x; }
|
||||
public long[] getArr1() { return arr1; }
|
||||
public long[][] getArr2() { return arr2; }
|
||||
public long getX() { return 0; }
|
||||
public long[] getArr1() { return null; }
|
||||
public long[][] getArr2() { return null; }
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -531,9 +532,9 @@ class AnnotationProcessorTest {
|
||||
float[] arr1; // Should be logged
|
||||
float[][] arr2; // Should not be logged
|
||||
|
||||
public float getX() { return x; }
|
||||
public float[] getArr1() { return arr1; }
|
||||
public float[][] getArr2() { return arr2; }
|
||||
public float getX() { return 0; }
|
||||
public float[] getArr1() { return null; }
|
||||
public float[][] getArr2() { return null; }
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -581,9 +582,9 @@ class AnnotationProcessorTest {
|
||||
double[][] arr2; // Should not be logged
|
||||
List<Double> list; // Should not be logged
|
||||
|
||||
public double getX() { return x; }
|
||||
public double[] getArr1() { return arr1; }
|
||||
public double[][] getArr2() { return arr2; }
|
||||
public double getX() { return 0; }
|
||||
public double[] getArr1() { return null; }
|
||||
public double[][] getArr2() { return null; }
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -630,9 +631,9 @@ class AnnotationProcessorTest {
|
||||
boolean[][] arr2; // Should not be logged
|
||||
List<Boolean> list; // Should not be logged
|
||||
|
||||
public boolean getX() { return x; }
|
||||
public boolean[] getArr1() { return arr1; }
|
||||
public boolean[][] getArr2() { return arr2; }
|
||||
public boolean getX() { return false; }
|
||||
public boolean[] getArr1() { return null; }
|
||||
public boolean[][] getArr2() { return null; }
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -680,9 +681,9 @@ class AnnotationProcessorTest {
|
||||
String[][] arr2; // Should not be logged
|
||||
List<String> list; // Should be logged
|
||||
|
||||
public String getX() { return x; }
|
||||
public String[] getArr1() { return arr1; }
|
||||
public String[][] getArr2() { return arr2; }
|
||||
public String getX() { return null; }
|
||||
public String[] getArr1() { return null; }
|
||||
public String[][] getArr2() { return null; }
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -739,9 +740,9 @@ class AnnotationProcessorTest {
|
||||
Structable[][] arr2; // Should not be logged
|
||||
List<Structable> list; // Should be logged
|
||||
|
||||
public Structable getX() { return x; }
|
||||
public Structable[] getArr1() { return arr1; }
|
||||
public Structable[][] getArr2() { return arr2; }
|
||||
public Structable getX() { return null; }
|
||||
public Structable[] getArr1() { return null; }
|
||||
public Structable[][] getArr2() { return null; }
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -910,6 +911,7 @@ class AnnotationProcessorTest {
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.withProcessors(new AnnotationProcessor())
|
||||
.compile(JavaFileObjects.forSourceString("edu.wpi.first.epilogue.Example", source));
|
||||
|
||||
@@ -1055,6 +1057,239 @@ class AnnotationProcessorTest {
|
||||
assertLoggerGenerates(source, expectedRootLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
void inheritanceOfLoggedTypes() {
|
||||
String source =
|
||||
"""
|
||||
package edu.wpi.first.epilogue;
|
||||
|
||||
@Logged
|
||||
interface IFace {}
|
||||
|
||||
@Logged
|
||||
class Impl1 implements IFace {}
|
||||
|
||||
@Logged
|
||||
class Impl2 implements IFace {}
|
||||
|
||||
@Logged
|
||||
interface I {
|
||||
int a();
|
||||
}
|
||||
|
||||
@Logged
|
||||
interface I2 extends I {
|
||||
int x();
|
||||
}
|
||||
|
||||
@Logged
|
||||
interface I3 extends I {
|
||||
int y();
|
||||
}
|
||||
|
||||
@Logged
|
||||
interface I4 extends I2, I3 {
|
||||
int z();
|
||||
}
|
||||
|
||||
@Logged
|
||||
class ConcreteLogged implements I4 {
|
||||
public int a() { return 0; }
|
||||
public int x() { return 0; }
|
||||
public int y() { return 0; }
|
||||
public int z() { return 0; }
|
||||
}
|
||||
|
||||
class ConcreteNotLogged implements I4 {
|
||||
public int a() { return 0; }
|
||||
public int x() { return 0; }
|
||||
public int y() { return 0; }
|
||||
public int z() { return 0; }
|
||||
}
|
||||
|
||||
@Logged
|
||||
public class Example {
|
||||
IFace asInterface;
|
||||
Impl1 firstImpl;
|
||||
Impl2 secondImpl;
|
||||
|
||||
I complex;
|
||||
}
|
||||
""";
|
||||
|
||||
String expectedRootLogger =
|
||||
"""
|
||||
package edu.wpi.first.epilogue;
|
||||
|
||||
import edu.wpi.first.epilogue.Logged;
|
||||
import edu.wpi.first.epilogue.Epilogue;
|
||||
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
|
||||
import edu.wpi.first.epilogue.logging.DataLogger;
|
||||
|
||||
public class ExampleLogger extends ClassSpecificLogger<Example> {
|
||||
public ExampleLogger() {
|
||||
super(Example.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(DataLogger dataLogger, Example object) {
|
||||
if (Epilogue.shouldLog(Logged.Importance.DEBUG)) {
|
||||
var $$asInterface = object.asInterface;
|
||||
if ($$asInterface instanceof edu.wpi.first.epilogue.Impl1 edu_wpi_first_epilogue_Impl1) {
|
||||
Epilogue.impl1Logger.tryUpdate(dataLogger.getSubLogger("asInterface"), edu_wpi_first_epilogue_Impl1, Epilogue.getConfig().errorHandler);
|
||||
} else if ($$asInterface instanceof edu.wpi.first.epilogue.Impl2 edu_wpi_first_epilogue_Impl2) {
|
||||
Epilogue.impl2Logger.tryUpdate(dataLogger.getSubLogger("asInterface"), edu_wpi_first_epilogue_Impl2, Epilogue.getConfig().errorHandler);
|
||||
} else {
|
||||
// Base type edu.wpi.first.epilogue.IFace
|
||||
Epilogue.iFaceLogger.tryUpdate(dataLogger.getSubLogger("asInterface"), $$asInterface, Epilogue.getConfig().errorHandler);
|
||||
};
|
||||
Epilogue.impl1Logger.tryUpdate(dataLogger.getSubLogger("firstImpl"), object.firstImpl, Epilogue.getConfig().errorHandler);
|
||||
Epilogue.impl2Logger.tryUpdate(dataLogger.getSubLogger("secondImpl"), object.secondImpl, Epilogue.getConfig().errorHandler);
|
||||
var $$complex = object.complex;
|
||||
if ($$complex instanceof edu.wpi.first.epilogue.ConcreteLogged edu_wpi_first_epilogue_ConcreteLogged) {
|
||||
Epilogue.concreteLoggedLogger.tryUpdate(dataLogger.getSubLogger("complex"), edu_wpi_first_epilogue_ConcreteLogged, Epilogue.getConfig().errorHandler);
|
||||
} else if ($$complex instanceof edu.wpi.first.epilogue.I4 edu_wpi_first_epilogue_I4) {
|
||||
Epilogue.i4Logger.tryUpdate(dataLogger.getSubLogger("complex"), edu_wpi_first_epilogue_I4, Epilogue.getConfig().errorHandler);
|
||||
} else if ($$complex instanceof edu.wpi.first.epilogue.I2 edu_wpi_first_epilogue_I2) {
|
||||
Epilogue.i2Logger.tryUpdate(dataLogger.getSubLogger("complex"), edu_wpi_first_epilogue_I2, Epilogue.getConfig().errorHandler);
|
||||
} else if ($$complex instanceof edu.wpi.first.epilogue.I3 edu_wpi_first_epilogue_I3) {
|
||||
Epilogue.i3Logger.tryUpdate(dataLogger.getSubLogger("complex"), edu_wpi_first_epilogue_I3, Epilogue.getConfig().errorHandler);
|
||||
} else {
|
||||
// Base type edu.wpi.first.epilogue.I
|
||||
Epilogue.iLogger.tryUpdate(dataLogger.getSubLogger("complex"), $$complex, Epilogue.getConfig().errorHandler);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
assertLoggerGenerates(source, expectedRootLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
void diamondInheritance() {
|
||||
String source =
|
||||
"""
|
||||
package edu.wpi.first.epilogue;
|
||||
|
||||
@Logged
|
||||
interface I {}
|
||||
|
||||
@Logged
|
||||
interface ExtendingInterface extends I {}
|
||||
|
||||
@Logged
|
||||
class Base implements I {}
|
||||
|
||||
/* Not @Logged */
|
||||
// Diamond inheritance from I (I -> ExtendingInterface -> Inheritor, I -> Base -> Inheritor)
|
||||
class Inheritor extends Base implements ExtendingInterface {}
|
||||
|
||||
@Logged
|
||||
class Example {
|
||||
// If this is set to an `Inheritor` instance, it will be logged as a `Base` object rather
|
||||
// than `ExtendingInterface` or `I`
|
||||
I theField;
|
||||
}
|
||||
""";
|
||||
|
||||
String expectedRootLogger =
|
||||
"""
|
||||
package edu.wpi.first.epilogue;
|
||||
|
||||
import edu.wpi.first.epilogue.Logged;
|
||||
import edu.wpi.first.epilogue.Epilogue;
|
||||
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
|
||||
import edu.wpi.first.epilogue.logging.DataLogger;
|
||||
|
||||
public class ExampleLogger extends ClassSpecificLogger<Example> {
|
||||
public ExampleLogger() {
|
||||
super(Example.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(DataLogger dataLogger, Example object) {
|
||||
if (Epilogue.shouldLog(Logged.Importance.DEBUG)) {
|
||||
var $$theField = object.theField;
|
||||
if ($$theField instanceof edu.wpi.first.epilogue.Base edu_wpi_first_epilogue_Base) {
|
||||
Epilogue.baseLogger.tryUpdate(dataLogger.getSubLogger("theField"), edu_wpi_first_epilogue_Base, Epilogue.getConfig().errorHandler);
|
||||
} else if ($$theField instanceof edu.wpi.first.epilogue.ExtendingInterface edu_wpi_first_epilogue_ExtendingInterface) {
|
||||
Epilogue.extendingInterfaceLogger.tryUpdate(dataLogger.getSubLogger("theField"), edu_wpi_first_epilogue_ExtendingInterface, Epilogue.getConfig().errorHandler);
|
||||
} else {
|
||||
// Base type edu.wpi.first.epilogue.I
|
||||
Epilogue.iLogger.tryUpdate(dataLogger.getSubLogger("theField"), $$theField, Epilogue.getConfig().errorHandler);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
assertLoggerGenerates(source, expectedRootLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
void instanceofChainWithField() {
|
||||
String source =
|
||||
"""
|
||||
package edu.wpi.first.epilogue;
|
||||
|
||||
@Logged
|
||||
interface I {}
|
||||
|
||||
@Logged
|
||||
class Base implements I {}
|
||||
|
||||
@Logged
|
||||
class Example {
|
||||
private I theField;
|
||||
}
|
||||
""";
|
||||
|
||||
String expectedRootLogger =
|
||||
"""
|
||||
package edu.wpi.first.epilogue;
|
||||
|
||||
import edu.wpi.first.epilogue.Logged;
|
||||
import edu.wpi.first.epilogue.Epilogue;
|
||||
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
|
||||
import edu.wpi.first.epilogue.logging.DataLogger;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
|
||||
public class ExampleLogger extends ClassSpecificLogger<Example> {
|
||||
private static final VarHandle $theField;
|
||||
|
||||
static {
|
||||
try {
|
||||
var lookup = MethodHandles.privateLookupIn(Example.class, MethodHandles.lookup());
|
||||
$theField = lookup.findVarHandle(Example.class, "theField", edu.wpi.first.epilogue.I.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("[EPILOGUE] Could not load private fields for logging!", e);
|
||||
}
|
||||
}
|
||||
|
||||
public ExampleLogger() {
|
||||
super(Example.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(DataLogger dataLogger, Example object) {
|
||||
if (Epilogue.shouldLog(Logged.Importance.DEBUG)) {
|
||||
var $$theField = (edu.wpi.first.epilogue.I) $theField.get(object);
|
||||
if ($$theField instanceof edu.wpi.first.epilogue.Base edu_wpi_first_epilogue_Base) {
|
||||
Epilogue.baseLogger.tryUpdate(dataLogger.getSubLogger("theField"), edu_wpi_first_epilogue_Base, Epilogue.getConfig().errorHandler);
|
||||
} else {
|
||||
// Base type edu.wpi.first.epilogue.I
|
||||
Epilogue.iLogger.tryUpdate(dataLogger.getSubLogger("theField"), $$theField, Epilogue.getConfig().errorHandler);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
assertLoggerGenerates(source, expectedRootLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customLogger() {
|
||||
String source =
|
||||
@@ -1123,6 +1358,7 @@ class AnnotationProcessorTest {
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.withProcessors(new AnnotationProcessor())
|
||||
.compile(JavaFileObjects.forSourceString("edu.wpi.first.epilogue.Example", source));
|
||||
|
||||
@@ -1135,6 +1371,187 @@ class AnnotationProcessorTest {
|
||||
message);
|
||||
}
|
||||
|
||||
@Test
|
||||
void loggingRecords() {
|
||||
String source =
|
||||
"""
|
||||
package edu.wpi.first.epilogue;
|
||||
|
||||
@Logged
|
||||
record Example(double x, double y) { }
|
||||
""";
|
||||
|
||||
String expectedRootLogger =
|
||||
"""
|
||||
package edu.wpi.first.epilogue;
|
||||
|
||||
import edu.wpi.first.epilogue.Logged;
|
||||
import edu.wpi.first.epilogue.Epilogue;
|
||||
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
|
||||
import edu.wpi.first.epilogue.logging.DataLogger;
|
||||
|
||||
public class ExampleLogger extends ClassSpecificLogger<Example> {
|
||||
public ExampleLogger() {
|
||||
super(Example.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(DataLogger dataLogger, Example object) {
|
||||
if (Epilogue.shouldLog(Logged.Importance.DEBUG)) {
|
||||
dataLogger.log("x", object.x());
|
||||
dataLogger.log("y", object.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
assertLoggerGenerates(source, expectedRootLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorsOnFieldNameConflicts() {
|
||||
String source =
|
||||
"""
|
||||
package edu.wpi.first.epilogue;
|
||||
|
||||
@Logged
|
||||
class Example {
|
||||
@Logged(name = "Custom Name") double x;
|
||||
@Logged(name = "Custom Name") double y;
|
||||
@Logged(name = "Custom Name") double z;
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withProcessors(new AnnotationProcessor())
|
||||
.compile(JavaFileObjects.forSourceString("edu.wpi.first.epilogue.Example", source));
|
||||
|
||||
assertThat(compilation).failed();
|
||||
assertThat(compilation).hadErrorCount(3);
|
||||
|
||||
List<Diagnostic<? extends JavaFileObject>> errors = compilation.errors();
|
||||
assertAll(
|
||||
() ->
|
||||
assertCompilationError(
|
||||
"[EPILOGUE] Conflicting name detected: \"Custom Name\" is also used by Example.y, Example.z",
|
||||
5,
|
||||
40,
|
||||
errors.get(0)),
|
||||
() ->
|
||||
assertCompilationError(
|
||||
"[EPILOGUE] Conflicting name detected: \"Custom Name\" is also used by Example.x, Example.z",
|
||||
6,
|
||||
40,
|
||||
errors.get(1)),
|
||||
() ->
|
||||
assertCompilationError(
|
||||
"[EPILOGUE] Conflicting name detected: \"Custom Name\" is also used by Example.x, Example.y",
|
||||
7,
|
||||
40,
|
||||
errors.get(2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotErrorOnGetterMethod() {
|
||||
String source =
|
||||
"""
|
||||
package edu.wpi.first.epilogue;
|
||||
|
||||
@Logged
|
||||
class Example {
|
||||
double x;
|
||||
public double x() { return x; }
|
||||
public double getX() { return x; }
|
||||
public double aTotallyArbitraryNameForAnAccessorMethod() { return x; }
|
||||
public double withANoOpTransform() { return x + 0; }
|
||||
public double withTemp() { var temp = x; return temp; }
|
||||
}
|
||||
""";
|
||||
|
||||
String expectedRootLogger =
|
||||
"""
|
||||
package edu.wpi.first.epilogue;
|
||||
|
||||
import edu.wpi.first.epilogue.Logged;
|
||||
import edu.wpi.first.epilogue.Epilogue;
|
||||
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
|
||||
import edu.wpi.first.epilogue.logging.DataLogger;
|
||||
|
||||
public class ExampleLogger extends ClassSpecificLogger<Example> {
|
||||
public ExampleLogger() {
|
||||
super(Example.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(DataLogger dataLogger, Example object) {
|
||||
if (Epilogue.shouldLog(Logged.Importance.DEBUG)) {
|
||||
dataLogger.log("x", object.x);
|
||||
dataLogger.log("withANoOpTransform", object.withANoOpTransform());
|
||||
dataLogger.log("withTemp", object.withTemp());
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
assertLoggerGenerates(source, expectedRootLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
void configuredDefaultNaming() {
|
||||
String source =
|
||||
"""
|
||||
package edu.wpi.first.epilogue;
|
||||
|
||||
@Logged(defaultNaming = Logged.Naming.USE_HUMAN_NAME)
|
||||
class Example {
|
||||
double m_memberPrefix;
|
||||
double kConstantPrefix;
|
||||
double k_otherConstantPrefix;
|
||||
double s_otherPrefix;
|
||||
|
||||
public double getTheGetterMethod() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Logged(defaultNaming = Logged.Naming.USE_CODE_NAME)
|
||||
public double optedOut() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
String expectedRootLogger =
|
||||
"""
|
||||
package edu.wpi.first.epilogue;
|
||||
|
||||
import edu.wpi.first.epilogue.Logged;
|
||||
import edu.wpi.first.epilogue.Epilogue;
|
||||
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
|
||||
import edu.wpi.first.epilogue.logging.DataLogger;
|
||||
|
||||
public class ExampleLogger extends ClassSpecificLogger<Example> {
|
||||
public ExampleLogger() {
|
||||
super(Example.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(DataLogger dataLogger, Example object) {
|
||||
if (Epilogue.shouldLog(Logged.Importance.DEBUG)) {
|
||||
dataLogger.log("Member Prefix", object.m_memberPrefix);
|
||||
dataLogger.log("Constant Prefix", object.kConstantPrefix);
|
||||
dataLogger.log("Other Constant Prefix", object.k_otherConstantPrefix);
|
||||
dataLogger.log("Other Prefix", object.s_otherPrefix);
|
||||
dataLogger.log("The Getter Method", object.getTheGetterMethod());
|
||||
dataLogger.log("optedOut", object.optedOut());
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
assertLoggerGenerates(source, expectedRootLogger);
|
||||
}
|
||||
|
||||
private void assertCompilationError(
|
||||
String message, long lineNumber, long col, Diagnostic<? extends JavaFileObject> diagnostic) {
|
||||
assertAll(
|
||||
@@ -1149,6 +1566,7 @@ class AnnotationProcessorTest {
|
||||
private void assertLoggerGenerates(String loggedClassContent, String loggerClassContent) {
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.withProcessors(new AnnotationProcessor())
|
||||
.compile(
|
||||
JavaFileObjects.forSourceString(
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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.List;
|
||||
|
||||
public class CompileTestOptions {
|
||||
public static final int kJavaVersion = 17;
|
||||
public static final List<Object> kJavaVersionOptions =
|
||||
List.of("-source", kJavaVersion, "-target", kJavaVersion);
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
|
||||
|
||||
import static com.google.testing.compile.CompilationSubject.assertThat;
|
||||
import static com.google.testing.compile.Compiler.javac;
|
||||
import static edu.wpi.first.epilogue.processor.CompileTestOptions.kJavaVersionOptions;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.google.testing.compile.Compilation;
|
||||
@@ -171,7 +172,7 @@ class EpilogueGeneratorTest {
|
||||
config.loggingPeriod = Seconds.of(robot.getPeriod());
|
||||
}
|
||||
if (config.loggingPeriodOffset == null) {
|
||||
config.loggingPeriodOffset = config.loggingPeriod.divide(2);
|
||||
config.loggingPeriodOffset = config.loggingPeriod.div(2);
|
||||
}
|
||||
|
||||
robot.addPeriodic(() -> {
|
||||
@@ -251,7 +252,7 @@ class EpilogueGeneratorTest {
|
||||
config.loggingPeriod = Seconds.of(robot.getPeriod());
|
||||
}
|
||||
if (config.loggingPeriodOffset == null) {
|
||||
config.loggingPeriodOffset = config.loggingPeriod.divide(2);
|
||||
config.loggingPeriodOffset = config.loggingPeriod.div(2);
|
||||
}
|
||||
|
||||
robot.addPeriodic(() -> {
|
||||
@@ -283,7 +284,7 @@ class EpilogueGeneratorTest {
|
||||
config.loggingPeriod = Seconds.of(robot.getPeriod());
|
||||
}
|
||||
if (config.loggingPeriodOffset == null) {
|
||||
config.loggingPeriodOffset = config.loggingPeriod.divide(2);
|
||||
config.loggingPeriodOffset = config.loggingPeriod.div(2);
|
||||
}
|
||||
|
||||
robot.addPeriodic(() -> {
|
||||
@@ -363,6 +364,7 @@ class EpilogueGeneratorTest {
|
||||
String loggedClassContent, String loggerClassContent) {
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.withProcessors(new AnnotationProcessor())
|
||||
.compile(JavaFileObjects.forSourceString("", loggedClassContent));
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
package edu.wpi.first.epilogue.processor;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class StringUtilsTest {
|
||||
@@ -16,4 +18,22 @@ class StringUtilsTest {
|
||||
assertEquals("fooBar", StringUtils.lowerCamelCase("FooBar"));
|
||||
assertEquals("allcaps", StringUtils.lowerCamelCase("ALLCAPS"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void splitToWords() {
|
||||
assertAll(
|
||||
() -> assertEquals(List.of("IO", "Logger"), StringUtils.splitToWords("IOLogger")),
|
||||
() -> assertEquals(List.of("LED", "Subsystem"), StringUtils.splitToWords("LEDSubsystem")),
|
||||
() -> assertEquals(List.of("Foo", "Bar"), StringUtils.splitToWords("FooBar")),
|
||||
() -> assertEquals(List.of("ALLCAPS"), StringUtils.splitToWords("ALLCAPS")),
|
||||
() ->
|
||||
assertEquals(List.of("k", "First", "Second"), StringUtils.splitToWords("kFirstSecond")),
|
||||
() ->
|
||||
assertEquals(
|
||||
List.of("there", "Is", "A", "Number", "123", "In", "Here", "VERSION", "456"),
|
||||
StringUtils.splitToWords("thereIsANumber123InHereVERSION456")),
|
||||
() ->
|
||||
assertEquals(
|
||||
List.of("get", "First", "Second"), StringUtils.splitToWords("getFirstSecond")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,4 +89,39 @@ public @interface Logged {
|
||||
* @return the importance of the annotated element
|
||||
*/
|
||||
Importance importance() default Importance.DEBUG;
|
||||
|
||||
/**
|
||||
* Different behaviors for how Epilogue will generate the names of logged data points. This only
|
||||
* applies to automatically generated names; any specific name provided with {@link #name()} will
|
||||
* take precedence over an automatically generated name.
|
||||
*/
|
||||
enum Naming {
|
||||
/**
|
||||
* Sets the default naming strategy to use the name of the element as it appears in source code.
|
||||
* For example, a field {@code double m_x} would be labeled as {@code "m_x"} by default, and a
|
||||
* {@code getX()} accessor would be labeled as {@code "getX"}.
|
||||
*/
|
||||
USE_CODE_NAME,
|
||||
|
||||
/**
|
||||
* Sets the default naming strategy to use a human-readable name based on the name of the name
|
||||
* of the element as it appears in source code. For example, a field {@code double m_x} would be
|
||||
* labeled as {@code "X"} by default, and a {@code getX()} accessor would also be labeled as
|
||||
* {@code "X"}. Because logged names must be unique, this configuration would fail to compile
|
||||
* and require either one of the fields to be excluded from logs (which, for simple accessors,
|
||||
* would be ideal to avoid duplicate data), or to rename one or both elements so the logged data
|
||||
* fields would have unique names.
|
||||
*/
|
||||
USE_HUMAN_NAME
|
||||
}
|
||||
|
||||
/**
|
||||
* The default naming behavior to use. Defaults to {@link Naming#USE_CODE_NAME}, which uses the
|
||||
* raw code name directly in logs. Any configuration of the {@link #name()} attribute on logged
|
||||
* fields and methods will take precedence over an automatically generated name.
|
||||
*
|
||||
* @return the naming strategy for and annotated field or method, or the default naming strategy
|
||||
* for all logged fields and methods in an annotated class
|
||||
*/
|
||||
Naming defaultNaming() default Naming.USE_CODE_NAME;
|
||||
}
|
||||
|
||||
@@ -69,19 +69,6 @@ tasks.withType(CppCompile) {
|
||||
|
||||
nativeUtils.exportsConfigs {
|
||||
glass {
|
||||
x64ExcludeSymbols = [
|
||||
'_CT??_R0?AV_System_error',
|
||||
'_CT??_R0?AVexception',
|
||||
'_CT??_R0?AVfailure',
|
||||
'_CT??_R0?AVruntime_error',
|
||||
'_CT??_R0?AVsystem_error',
|
||||
'_CTA5?AVfailure',
|
||||
'_TI5?AVfailure',
|
||||
'_CT??_R0?AVout_of_range',
|
||||
'_CTA3?AVout_of_range',
|
||||
'_TI3?AVout_of_range',
|
||||
'_CT??_R0?AVbad_cast'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ static void WorkspaceResetImpl() {
|
||||
|
||||
// clear storage
|
||||
for (auto&& root : gContext->storageRoots) {
|
||||
root.second->Clear();
|
||||
root.second.Clear();
|
||||
}
|
||||
|
||||
// ImGui reset
|
||||
@@ -53,7 +53,7 @@ static void WorkspaceInit() {
|
||||
}
|
||||
|
||||
for (auto&& root : gContext->storageRoots) {
|
||||
root.getValue()->Apply();
|
||||
root.second.Apply();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,19 +152,14 @@ static bool LoadStorageRootImpl(Context* ctx, const std::string& filename,
|
||||
fileBuffer.error().message().c_str());
|
||||
return false;
|
||||
}
|
||||
auto& storage = ctx->storageRoots[rootName];
|
||||
bool createdStorage = false;
|
||||
if (!storage) {
|
||||
storage = std::make_unique<Storage>();
|
||||
createdStorage = true;
|
||||
}
|
||||
auto [it, createdStorage] = ctx->storageRoots.try_emplace(rootName);
|
||||
try {
|
||||
storage->FromJson(wpi::json::parse(fileBuffer.value()->GetCharBuffer()),
|
||||
filename.c_str());
|
||||
it->second.FromJson(wpi::json::parse(fileBuffer.value()->GetCharBuffer()),
|
||||
filename.c_str());
|
||||
} catch (wpi::json::parse_error& e) {
|
||||
ImGui::LogText("Error loading %s: %s", filename.c_str(), e.what());
|
||||
if (createdStorage) {
|
||||
ctx->storageRoots.erase(rootName);
|
||||
ctx->storageRoots.erase(it);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -176,7 +171,7 @@ static bool LoadStorageImpl(Context* ctx, std::string_view dir,
|
||||
bool rv = true;
|
||||
for (auto&& root : ctx->storageRoots) {
|
||||
std::string filename;
|
||||
auto rootName = root.getKey();
|
||||
auto& rootName = root.first;
|
||||
if (rootName.empty()) {
|
||||
filename = (fs::path{dir} / fmt::format("{}.json", name)).string();
|
||||
} else {
|
||||
@@ -291,7 +286,7 @@ static bool SaveStorageImpl(Context* ctx, std::string_view dir,
|
||||
if (exiting && wpi::gui::gContext->resetOnExit) {
|
||||
fs::remove(dirPath / fmt::format("{}-window.json", name), ec);
|
||||
for (auto&& root : ctx->storageRoots) {
|
||||
auto rootName = root.getKey();
|
||||
auto& rootName = root.first;
|
||||
if (rootName.empty()) {
|
||||
fs::remove(dirPath / fmt::format("{}.json", name), ec);
|
||||
} else {
|
||||
@@ -304,14 +299,14 @@ static bool SaveStorageImpl(Context* ctx, std::string_view dir,
|
||||
(dirPath / fmt::format("{}-window.json", name)).string());
|
||||
|
||||
for (auto&& root : ctx->storageRoots) {
|
||||
auto rootName = root.getKey();
|
||||
auto& rootName = root.first;
|
||||
std::string filename;
|
||||
if (rootName.empty()) {
|
||||
filename = (dirPath / fmt::format("{}.json", name)).string();
|
||||
} else {
|
||||
filename = (dirPath / fmt::format("{}-{}.json", name, rootName)).string();
|
||||
}
|
||||
if (!SaveStorageRootImpl(ctx, filename, *root.getValue())) {
|
||||
if (!SaveStorageRootImpl(ctx, filename, root.second)) {
|
||||
rv = false;
|
||||
}
|
||||
}
|
||||
@@ -319,10 +314,9 @@ static bool SaveStorageImpl(Context* ctx, std::string_view dir,
|
||||
}
|
||||
|
||||
Context::Context()
|
||||
: sourceNameStorage{storageRoots.insert({"", std::make_unique<Storage>()})
|
||||
.first->getValue()
|
||||
->GetChild("sourceNames")} {
|
||||
storageStack.emplace_back(storageRoots[""].get());
|
||||
: sourceNameStorage{
|
||||
storageRoots.try_emplace("").first->second.GetChild("sourceNames")} {
|
||||
storageStack.emplace_back(&storageRoots[""]);
|
||||
|
||||
// override ImGui ini saving
|
||||
wpi::gui::ConfigureCustomSaveSettings(
|
||||
@@ -435,11 +429,7 @@ Storage& glass::GetCurStorageRoot() {
|
||||
}
|
||||
|
||||
Storage& glass::GetStorageRoot(std::string_view rootName) {
|
||||
auto& storage = gContext->storageRoots[rootName];
|
||||
if (!storage) {
|
||||
storage = std::make_unique<Storage>();
|
||||
}
|
||||
return *storage;
|
||||
return gContext->storageRoots[rootName];
|
||||
}
|
||||
|
||||
void glass::ResetStorageStack(std::string_view rootName) {
|
||||
|
||||
@@ -112,5 +112,5 @@ DataSource* DataSource::Find(std::string_view id) {
|
||||
if (it == gContext->sources.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->getValue();
|
||||
return it->second;
|
||||
}
|
||||
|
||||
@@ -331,7 +331,7 @@ std::vector<std::unique_ptr<Storage>>& Storage::GetChildArray(
|
||||
std::unique_ptr<Storage::Value> Storage::Erase(std::string_view key) {
|
||||
auto it = m_values.find(key);
|
||||
if (it != m_values.end()) {
|
||||
auto rv = std::move(it->getValue());
|
||||
auto rv = std::move(it->second);
|
||||
m_values.erase(it);
|
||||
return rv;
|
||||
}
|
||||
@@ -339,11 +339,9 @@ std::unique_ptr<Storage::Value> Storage::Erase(std::string_view key) {
|
||||
}
|
||||
|
||||
void Storage::EraseChildren() {
|
||||
for (auto&& kv : m_values) {
|
||||
if (kv.getValue()->type == Value::kChild) {
|
||||
m_values.remove(&kv);
|
||||
}
|
||||
}
|
||||
std::erase_if(m_values, [](const auto& kv) {
|
||||
return kv.second->type == Value::kChild;
|
||||
});
|
||||
}
|
||||
|
||||
static bool JsonArrayToStorage(Storage::Value* valuePtr, const wpi::json& jarr,
|
||||
@@ -559,7 +557,7 @@ wpi::json Storage::ToJson() const {
|
||||
wpi::json j = wpi::json::object();
|
||||
for (auto&& kv : m_values) {
|
||||
wpi::json jelem;
|
||||
auto& value = *kv.getValue();
|
||||
auto& value = *kv.second;
|
||||
switch (value.type) {
|
||||
#define CASE(CapsName, LowerName) \
|
||||
case Value::k##CapsName: \
|
||||
@@ -602,7 +600,7 @@ wpi::json Storage::ToJson() const {
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
j.emplace(kv.getKey(), std::move(jelem));
|
||||
j.emplace(kv.first, std::move(jelem));
|
||||
}
|
||||
return j;
|
||||
}
|
||||
@@ -617,7 +615,7 @@ void Storage::Clear() {
|
||||
|
||||
void Storage::ClearValues() {
|
||||
for (auto&& kv : m_values) {
|
||||
auto& value = *kv.getValue();
|
||||
auto& value = *kv.second;
|
||||
switch (value.type) {
|
||||
case Value::kInt:
|
||||
value.intVal = value.intDefault;
|
||||
@@ -703,7 +701,7 @@ void Storage::Apply() {
|
||||
|
||||
void Storage::ApplyChildren() {
|
||||
for (auto&& kv : m_values) {
|
||||
auto& value = *kv.getValue();
|
||||
auto& value = *kv.second;
|
||||
switch (value.type) {
|
||||
case Value::kChild:
|
||||
value.child->Apply();
|
||||
|
||||
32
glass/src/lib/native/cpp/other/Alerts.cpp
Normal file
32
glass/src/lib/native/cpp/other/Alerts.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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.
|
||||
|
||||
#include "glass/other/Alerts.h"
|
||||
|
||||
#include <IconsFontAwesome6.h>
|
||||
#include <imgui.h>
|
||||
|
||||
using namespace glass;
|
||||
|
||||
void glass::DisplayAlerts(AlertsModel* model) {
|
||||
auto& infos = model->GetInfos();
|
||||
auto& warnings = model->GetWarnings();
|
||||
auto& errors = model->GetErrors();
|
||||
|
||||
const ImVec4 kWarningColor{0.85f, 0.56f, 0.0f, 1.0f};
|
||||
const ImVec4 kErrorColor{0.9f, 0.25f, 0.25f, 1.0f};
|
||||
|
||||
// show higher severity alerts on top
|
||||
for (auto&& error : errors) {
|
||||
ImGui::TextColored(kErrorColor, "%s %s", ICON_FA_CIRCLE_XMARK,
|
||||
error.c_str());
|
||||
}
|
||||
for (auto&& warning : warnings) {
|
||||
ImGui::TextColored(kWarningColor, "%s %s", ICON_FA_TRIANGLE_EXCLAMATION,
|
||||
warning.c_str());
|
||||
}
|
||||
for (auto&& info : infos) {
|
||||
ImGui::Text("%s %s", ICON_FA_CIRCLE_INFO, info.c_str());
|
||||
}
|
||||
}
|
||||
@@ -235,7 +235,7 @@ class FieldInfo {
|
||||
FieldFrameData GetFrameData(ImVec2 min, ImVec2 max) const;
|
||||
void Draw(ImDrawList* drawList, const FieldFrameData& frameData) const;
|
||||
|
||||
wpi::StringMap<std::unique_ptr<ObjectInfo>> m_objects;
|
||||
wpi::StringMap<ObjectInfo> m_objects;
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
@@ -591,6 +591,12 @@ FieldFrameData FieldInfo::GetFrameData(ImVec2 min, ImVec2 max) const {
|
||||
max.x -= 20;
|
||||
min.y += 20;
|
||||
max.y -= 20;
|
||||
|
||||
// also pad the image so it's the same size as the box
|
||||
ffd.imageMin.x += 20;
|
||||
ffd.imageMax.x -= 20;
|
||||
ffd.imageMin.y += 20;
|
||||
ffd.imageMax.y -= 20;
|
||||
}
|
||||
|
||||
ffd.min = min;
|
||||
@@ -946,15 +952,12 @@ void glass::DisplayField2DSettings(Field2DModel* model) {
|
||||
return;
|
||||
}
|
||||
PushID(name);
|
||||
auto& objRef = field->m_objects[name];
|
||||
if (!objRef) {
|
||||
objRef = std::make_unique<ObjectInfo>(GetStorage());
|
||||
}
|
||||
auto obj = objRef.get();
|
||||
|
||||
wpi::SmallString<64> nameBuf{name};
|
||||
if (ImGui::CollapsingHeader(nameBuf.c_str())) {
|
||||
obj->DisplaySettings();
|
||||
auto& obj =
|
||||
field->m_objects.try_emplace(name, GetStorage()).first->second;
|
||||
obj.DisplaySettings();
|
||||
}
|
||||
PopID();
|
||||
});
|
||||
@@ -1090,14 +1093,10 @@ void FieldDisplay::Display(FieldInfo* field, Field2DModel* model,
|
||||
void FieldDisplay::DisplayObject(FieldObjectModel& model,
|
||||
std::string_view name) {
|
||||
PushID(name);
|
||||
auto& objRef = m_field->m_objects[name];
|
||||
if (!objRef) {
|
||||
objRef = std::make_unique<ObjectInfo>(GetStorage());
|
||||
}
|
||||
auto obj = objRef.get();
|
||||
obj->LoadImage();
|
||||
auto& obj = m_field->m_objects.try_emplace(name, GetStorage()).first->second;
|
||||
obj.LoadImage();
|
||||
|
||||
auto displayOptions = obj->GetDisplayOptions();
|
||||
auto displayOptions = obj.GetDisplayOptions();
|
||||
|
||||
m_centerLine.resize(0);
|
||||
m_leftLine.resize(0);
|
||||
@@ -1134,9 +1133,9 @@ void FieldDisplay::DisplayObject(FieldObjectModel& model,
|
||||
}
|
||||
|
||||
m_drawSplit.SetCurrentChannel(m_drawList, 0);
|
||||
obj->DrawLine(m_drawList, m_centerLine);
|
||||
obj->DrawLine(m_drawList, m_leftLine);
|
||||
obj->DrawLine(m_drawList, m_rightLine);
|
||||
obj.DrawLine(m_drawList, m_centerLine);
|
||||
obj.DrawLine(m_drawList, m_leftLine);
|
||||
obj.DrawLine(m_drawList, m_rightLine);
|
||||
m_drawSplit.Merge(m_drawList);
|
||||
|
||||
PopID();
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -34,7 +33,7 @@ class Context {
|
||||
std::string storageAutoSaveDir = ".";
|
||||
std::string storageName = "imgui";
|
||||
wpi::SmallVector<Storage*, 32> storageStack;
|
||||
wpi::StringMap<std::unique_ptr<Storage>> storageRoots;
|
||||
wpi::StringMap<Storage> storageRoots;
|
||||
wpi::StringMap<bool> deviceHidden;
|
||||
wpi::StringMap<DataSource*> sources;
|
||||
Storage& sourceNameStorage;
|
||||
|
||||
@@ -62,7 +62,10 @@ class Provider : public WindowManager {
|
||||
* Perform global initialization. This should be called prior to
|
||||
* wpi::gui::Initialize().
|
||||
*/
|
||||
void GlobalInit() override;
|
||||
void GlobalInit() override {
|
||||
WindowManager::GlobalInit();
|
||||
wpi::gui::AddEarlyExecute([this] { Update(); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the specified view by default on first load. Has no effect if
|
||||
@@ -70,7 +73,17 @@ class Provider : public WindowManager {
|
||||
*
|
||||
* @param name View name
|
||||
*/
|
||||
void ShowDefault(std::string_view name);
|
||||
void ShowDefault(std::string_view name) {
|
||||
auto win = GetWindow(name);
|
||||
if (win) {
|
||||
return;
|
||||
}
|
||||
auto it = FindViewEntry(name);
|
||||
if (it == m_viewEntries.end() || (*it)->name != name) {
|
||||
return;
|
||||
}
|
||||
(*it)->showDefault = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a model and view combination. Equivalent to calling both
|
||||
@@ -82,7 +95,10 @@ class Provider : public WindowManager {
|
||||
* @param createView Functor for creating view
|
||||
*/
|
||||
void Register(std::string_view name, ExistsFunc exists,
|
||||
CreateModelFunc createModel, CreateViewFunc createView);
|
||||
CreateModelFunc createModel, CreateViewFunc createView) {
|
||||
RegisterModel(name, std::move(exists), std::move(createModel));
|
||||
RegisterView(name, name, nullptr, std::move(createView));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a model.
|
||||
@@ -92,7 +108,16 @@ class Provider : public WindowManager {
|
||||
* @param createModel Functor for creating model
|
||||
*/
|
||||
void RegisterModel(std::string_view name, ExistsFunc exists,
|
||||
CreateModelFunc createModel);
|
||||
CreateModelFunc createModel) {
|
||||
auto it = FindModelEntry(name);
|
||||
// ignore if exists
|
||||
if (it != m_modelEntries.end() && (*it)->name == name) {
|
||||
return;
|
||||
}
|
||||
// insert in sorted location
|
||||
m_modelEntries.emplace(
|
||||
it, MakeModelEntry(name, std::move(exists), std::move(createModel)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a view.
|
||||
@@ -103,10 +128,33 @@ class Provider : public WindowManager {
|
||||
* @param createView Functor for creating view
|
||||
*/
|
||||
void RegisterView(std::string_view name, std::string_view modelName,
|
||||
ViewExistsFunc exists, CreateViewFunc createView);
|
||||
ViewExistsFunc exists, CreateViewFunc createView) {
|
||||
// find model; if model doesn't exist, ignore
|
||||
auto modelIt = FindModelEntry(modelName);
|
||||
if (modelIt == m_modelEntries.end() || (*modelIt)->name != modelName) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto viewIt = FindViewEntry(name);
|
||||
// ignore if exists
|
||||
if (viewIt != m_viewEntries.end() && (*viewIt)->name == name) {
|
||||
return;
|
||||
}
|
||||
// insert in sorted location
|
||||
m_viewEntries.emplace(viewIt,
|
||||
MakeViewEntry(name, modelIt->get(), std::move(exists),
|
||||
std::move(createView)));
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void Update();
|
||||
virtual void Update() {
|
||||
// update entries
|
||||
for (auto&& entry : m_modelEntries) {
|
||||
if (entry->model) {
|
||||
entry->model->Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ModelEntry {
|
||||
ModelEntry(std::string_view name, ExistsFunc exists,
|
||||
@@ -145,8 +193,17 @@ class Provider : public WindowManager {
|
||||
using ViewEntries = std::vector<std::unique_ptr<ViewEntry>>;
|
||||
ViewEntries m_viewEntries;
|
||||
|
||||
typename ModelEntries::iterator FindModelEntry(std::string_view name);
|
||||
typename ViewEntries::iterator FindViewEntry(std::string_view name);
|
||||
typename ModelEntries::iterator FindModelEntry(std::string_view name) {
|
||||
return std::lower_bound(
|
||||
m_modelEntries.begin(), m_modelEntries.end(), name,
|
||||
[](const auto& elem, std::string_view s) { return elem->name < s; });
|
||||
}
|
||||
|
||||
typename ViewEntries::iterator FindViewEntry(std::string_view name) {
|
||||
return std::lower_bound(
|
||||
m_viewEntries.begin(), m_viewEntries.end(), name,
|
||||
[](const auto& elem, std::string_view s) { return elem->name < s; });
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<ModelEntry> MakeModelEntry(
|
||||
std::string_view name, ExistsFunc exists, CreateModelFunc createModel) {
|
||||
@@ -166,5 +223,3 @@ class Provider : public WindowManager {
|
||||
};
|
||||
|
||||
} // namespace glass
|
||||
|
||||
#include "Provider.inc"
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "glass/Provider.h"
|
||||
|
||||
namespace glass {
|
||||
|
||||
template <typename Functions>
|
||||
void Provider<Functions>::GlobalInit() {
|
||||
WindowManager::GlobalInit();
|
||||
wpi::gui::AddEarlyExecute([this] { Update(); });
|
||||
}
|
||||
|
||||
template <typename Functions>
|
||||
void Provider<Functions>::ShowDefault(std::string_view name) {
|
||||
auto win = GetWindow(name);
|
||||
if (win) {
|
||||
return;
|
||||
}
|
||||
auto it = FindViewEntry(name);
|
||||
if (it == m_viewEntries.end() || (*it)->name != name) {
|
||||
return;
|
||||
}
|
||||
(*it)->showDefault = true;
|
||||
}
|
||||
|
||||
template <typename Functions>
|
||||
void Provider<Functions>::Register(std::string_view name, ExistsFunc exists,
|
||||
CreateModelFunc createModel,
|
||||
CreateViewFunc createView) {
|
||||
RegisterModel(name, std::move(exists), std::move(createModel));
|
||||
RegisterView(name, name, nullptr, std::move(createView));
|
||||
}
|
||||
|
||||
template <typename Functions>
|
||||
void Provider<Functions>::RegisterModel(std::string_view name,
|
||||
ExistsFunc exists,
|
||||
CreateModelFunc createModel) {
|
||||
auto it = FindModelEntry(name);
|
||||
// ignore if exists
|
||||
if (it != m_modelEntries.end() && (*it)->name == name) {
|
||||
return;
|
||||
}
|
||||
// insert in sorted location
|
||||
m_modelEntries.emplace(
|
||||
it, MakeModelEntry(name, std::move(exists), std::move(createModel)));
|
||||
}
|
||||
|
||||
template <typename Functions>
|
||||
void Provider<Functions>::RegisterView(std::string_view name,
|
||||
std::string_view modelName,
|
||||
ViewExistsFunc exists,
|
||||
CreateViewFunc createView) {
|
||||
// find model; if model doesn't exist, ignore
|
||||
auto modelIt = FindModelEntry(modelName);
|
||||
if (modelIt == m_modelEntries.end() || (*modelIt)->name != modelName) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto viewIt = FindViewEntry(name);
|
||||
// ignore if exists
|
||||
if (viewIt != m_viewEntries.end() && (*viewIt)->name == name) {
|
||||
return;
|
||||
}
|
||||
// insert in sorted location
|
||||
m_viewEntries.emplace(viewIt,
|
||||
MakeViewEntry(name, modelIt->get(), std::move(exists),
|
||||
std::move(createView)));
|
||||
}
|
||||
|
||||
template <typename Functions>
|
||||
void Provider<Functions>::Update() {
|
||||
// update entries
|
||||
for (auto&& entry : m_modelEntries) {
|
||||
if (entry->model) {
|
||||
entry->model->Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Functions>
|
||||
typename Provider<Functions>::ModelEntries::iterator
|
||||
Provider<Functions>::FindModelEntry(std::string_view name) {
|
||||
return std::lower_bound(
|
||||
m_modelEntries.begin(), m_modelEntries.end(), name,
|
||||
[](const auto& elem, std::string_view s) { return elem->name < s; });
|
||||
}
|
||||
|
||||
template <typename Functions>
|
||||
typename Provider<Functions>::ViewEntries::iterator
|
||||
Provider<Functions>::FindViewEntry(std::string_view name) {
|
||||
return std::lower_bound(
|
||||
m_viewEntries.begin(), m_viewEntries.end(), name,
|
||||
[](const auto& elem, std::string_view s) { return elem->name < s; });
|
||||
}
|
||||
|
||||
} // namespace glass
|
||||
@@ -249,8 +249,7 @@ class ChildIterator {
|
||||
public:
|
||||
ChildIterator(IteratorType it, IteratorType end) noexcept
|
||||
: anchor(it), end(end) {
|
||||
while (anchor != end &&
|
||||
anchor->getValue()->type != Storage::Value::kChild) {
|
||||
while (anchor != end && anchor->second->type != Storage::Value::kChild) {
|
||||
++anchor;
|
||||
}
|
||||
}
|
||||
@@ -261,8 +260,7 @@ class ChildIterator {
|
||||
/// increment operator (needed for range-based for)
|
||||
ChildIterator& operator++() {
|
||||
++anchor;
|
||||
while (anchor != end &&
|
||||
anchor->getValue()->type != Storage::Value::kChild) {
|
||||
while (anchor != end && anchor->second->type != Storage::Value::kChild) {
|
||||
++anchor;
|
||||
}
|
||||
return *this;
|
||||
@@ -274,10 +272,10 @@ class ChildIterator {
|
||||
}
|
||||
|
||||
/// return key of the iterator
|
||||
std::string_view key() const { return anchor->getKey(); }
|
||||
std::string_view key() const { return anchor->first; }
|
||||
|
||||
/// return value of the iterator
|
||||
Storage& value() const { return *anchor->getValue()->child; }
|
||||
Storage& value() const { return *anchor->second->child; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
23
glass/src/lib/native/include/glass/other/Alerts.h
Normal file
23
glass/src/lib/native/include/glass/other/Alerts.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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>
|
||||
#include <vector>
|
||||
|
||||
#include "glass/Model.h"
|
||||
|
||||
namespace glass {
|
||||
|
||||
class AlertsModel : public Model {
|
||||
public:
|
||||
virtual const std::vector<std::string>& GetInfos() = 0;
|
||||
virtual const std::vector<std::string>& GetWarnings() = 0;
|
||||
virtual const std::vector<std::string>& GetErrors() = 0;
|
||||
};
|
||||
|
||||
void DisplayAlerts(AlertsModel* model);
|
||||
|
||||
} // namespace glass
|
||||
51
glass/src/libnt/native/cpp/NTAlerts.cpp
Normal file
51
glass/src/libnt/native/cpp/NTAlerts.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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.
|
||||
|
||||
#include "glass/networktables/NTAlerts.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
using namespace glass;
|
||||
|
||||
NTAlertsModel::NTAlertsModel(std::string_view path)
|
||||
: NTAlertsModel{nt::NetworkTableInstance::GetDefault(), path} {}
|
||||
|
||||
NTAlertsModel::NTAlertsModel(nt::NetworkTableInstance inst,
|
||||
std::string_view path)
|
||||
: m_inst{inst},
|
||||
m_infos{m_inst.GetStringArrayTopic(fmt::format("{}/infos", path))
|
||||
.Subscribe({})},
|
||||
m_warnings{m_inst.GetStringArrayTopic(fmt::format("{}/warnings", path))
|
||||
.Subscribe({})},
|
||||
m_errors{m_inst.GetStringArrayTopic(fmt::format("{}/errors", path))
|
||||
.Subscribe({})} {}
|
||||
|
||||
void NTAlertsModel::Update() {
|
||||
if (!m_infos.Exists()) {
|
||||
m_infosValue.clear();
|
||||
}
|
||||
for (auto&& v : m_infos.ReadQueue()) {
|
||||
m_infosValue = std::move(v.value);
|
||||
}
|
||||
|
||||
if (!m_warnings.Exists()) {
|
||||
m_warningsValue.clear();
|
||||
}
|
||||
for (auto&& v : m_warnings.ReadQueue()) {
|
||||
m_warningsValue = std::move(v.value);
|
||||
}
|
||||
|
||||
if (!m_errors.Exists()) {
|
||||
m_errorsValue.clear();
|
||||
}
|
||||
for (auto&& v : m_errors.ReadQueue()) {
|
||||
m_errorsValue = std::move(v.value);
|
||||
}
|
||||
}
|
||||
|
||||
bool NTAlertsModel::Exists() {
|
||||
return m_infos.Exists();
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "glass/networktables/NTAlerts.h"
|
||||
#include "glass/networktables/NTCommandScheduler.h"
|
||||
#include "glass/networktables/NTCommandSelector.h"
|
||||
#include "glass/networktables/NTDifferentialDrive.h"
|
||||
@@ -24,6 +25,16 @@
|
||||
using namespace glass;
|
||||
|
||||
void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
|
||||
provider.Register(
|
||||
NTAlertsModel::kType,
|
||||
[](nt::NetworkTableInstance inst, const char* path) {
|
||||
return std::make_unique<NTAlertsModel>(inst, path);
|
||||
},
|
||||
[](Window* win, Model* model, const char*) {
|
||||
win->SetDefaultSize(300, 150);
|
||||
return MakeFunctionView(
|
||||
[=] { DisplayAlerts(static_cast<NTAlertsModel*>(model)); });
|
||||
});
|
||||
provider.Register(
|
||||
NTCommandSchedulerModel::kType,
|
||||
[](nt::NetworkTableInstance inst, const char* path) {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <networktables/StringArrayTopic.h>
|
||||
|
||||
#include "glass/other/Alerts.h"
|
||||
|
||||
namespace glass {
|
||||
|
||||
class NTAlertsModel : public AlertsModel {
|
||||
public:
|
||||
static constexpr const char* kType = "Alerts";
|
||||
|
||||
// path is to the table containing ".type", excluding the trailing /
|
||||
explicit NTAlertsModel(std::string_view path);
|
||||
NTAlertsModel(nt::NetworkTableInstance inst, std::string_view path);
|
||||
|
||||
const std::vector<std::string>& GetInfos() override { return m_infosValue; }
|
||||
|
||||
const std::vector<std::string>& GetWarnings() override {
|
||||
return m_warningsValue;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& GetErrors() override { return m_errorsValue; }
|
||||
|
||||
void Update() override;
|
||||
bool Exists() override;
|
||||
bool IsReadOnly() override { return false; }
|
||||
|
||||
private:
|
||||
nt::NetworkTableInstance m_inst;
|
||||
nt::StringArraySubscriber m_infos;
|
||||
nt::StringArraySubscriber m_warnings;
|
||||
nt::StringArraySubscriber m_errors;
|
||||
|
||||
std::vector<std::string> m_infosValue;
|
||||
std::vector<std::string> m_warningsValue;
|
||||
std::vector<std::string> m_errorsValue;
|
||||
};
|
||||
|
||||
} // namespace glass
|
||||
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.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
7
gradlew
vendored
7
gradlew
vendored
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -84,7 +86,8 @@ 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 "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
22
gradlew.bat
vendored
22
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
||||
91
hal/BUILD.bazel
Normal file
91
hal/BUILD.bazel
Normal file
@@ -0,0 +1,91 @@
|
||||
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
|
||||
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
|
||||
|
||||
cc_library(
|
||||
name = "generated_cc_headers",
|
||||
hdrs = glob(["src/generated/main/native/include/**"]),
|
||||
includes = ["src/generated/main/native/include"],
|
||||
strip_include_prefix = "src/generated/main/native/include",
|
||||
visibility = ["//hal:__subpackages__"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "generated_java",
|
||||
srcs = glob(["src/generated/main/java/**/*.java"]),
|
||||
visibility = ["//hal:__subpackages__"],
|
||||
)
|
||||
|
||||
ATHENA_SRCS = glob(["src/main/native/athena/**"])
|
||||
|
||||
ATHENA_DEPS = ["@bzlmodrio-ni//libraries/cpp/ni:shared"]
|
||||
|
||||
SIM_SRCS = glob(["src/main/native/sim/**"])
|
||||
|
||||
SIM_DEPS = []
|
||||
|
||||
HAL_DEPS = select({
|
||||
"@rules_bzlmodrio_toolchains//constraints/is_roborio:roborio": ATHENA_DEPS,
|
||||
"//conditions:default": SIM_DEPS,
|
||||
})
|
||||
|
||||
filegroup(
|
||||
name = "platform-srcs",
|
||||
srcs = select({
|
||||
"@rules_bzlmodrio_toolchains//constraints/is_roborio:roborio": ATHENA_SRCS,
|
||||
"//conditions:default": SIM_SRCS,
|
||||
}),
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "wpiHal.static",
|
||||
srcs = [":platform-srcs"] + glob(
|
||||
["src/main/native/cpp/**"],
|
||||
exclude = ["src/main/native/cpp/jni/**"],
|
||||
),
|
||||
hdrs = glob(["src/main/native/include/**/*"]),
|
||||
includes = ["src/main/native/include"],
|
||||
strip_include_prefix = "src/main/native/include",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":generated_cc_headers",
|
||||
"//wpiutil:wpiutil.static",
|
||||
] + HAL_DEPS,
|
||||
)
|
||||
|
||||
java_library(
|
||||
name = "hal-java",
|
||||
srcs = [":generated_java"] + glob(["src/main/java/**/*.java"]),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//wpiutil:wpiutil-java",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "hal-cpp-test",
|
||||
size = "small",
|
||||
srcs = glob([
|
||||
"src/test/native/**/*.cpp",
|
||||
"src/test/native/**/*.h",
|
||||
]),
|
||||
deps = [
|
||||
":wpiHal.static",
|
||||
"//thirdparty/googletest:googletest.static",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "DevMain-Cpp",
|
||||
srcs = ["src/dev/native/cpp/main.cpp"],
|
||||
deps = [
|
||||
":wpiHal.static",
|
||||
],
|
||||
)
|
||||
|
||||
java_binary(
|
||||
name = "DevMain-Java",
|
||||
srcs = ["src/dev/java/edu/wpi/first/hal/DevMain.java"],
|
||||
main_class = "edu.wpi.first.hal.DevMain",
|
||||
deps = [
|
||||
],
|
||||
)
|
||||
@@ -74,19 +74,6 @@ Action<List<String>> symbolFilter = { symbols ->
|
||||
|
||||
nativeUtils.exportsConfigs {
|
||||
hal {
|
||||
x64ExcludeSymbols = [
|
||||
'_CT??_R0?AV_System_error',
|
||||
'_CT??_R0?AVexception',
|
||||
'_CT??_R0?AVfailure',
|
||||
'_CT??_R0?AVruntime_error',
|
||||
'_CT??_R0?AVsystem_error',
|
||||
'_CTA5?AVfailure',
|
||||
'_TI5?AVfailure',
|
||||
'_CT??_R0?AVout_of_range',
|
||||
'_CTA3?AVout_of_range',
|
||||
'_TI3?AVout_of_range',
|
||||
'_CT??_R0?AVbad_cast'
|
||||
]
|
||||
}
|
||||
halJNI {
|
||||
x64SymbolFilter = symbolFilter
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
# 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.
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def generate_usage_reporting(output_directory: Path, template_directory: Path):
|
||||
@@ -49,7 +49,7 @@ def generate_usage_reporting(output_directory: Path, template_directory: Path):
|
||||
)
|
||||
|
||||
frc_net_comm = output_directory / f"main/java/{java_package}/FRCNetComm.java"
|
||||
frc_net_comm.write_text(contents, encoding="utf-8")
|
||||
frc_net_comm.write_text(contents, encoding="utf-8", newline="\n")
|
||||
|
||||
with (template_directory / "FRCUsageReporting.h.in").open(
|
||||
encoding="utf-8"
|
||||
@@ -65,11 +65,26 @@ def generate_usage_reporting(output_directory: Path, template_directory: Path):
|
||||
usage_reporting_hdr = (
|
||||
output_directory / "main/native/include/hal/FRCUsageReporting.h"
|
||||
)
|
||||
usage_reporting_hdr.write_text(contents, encoding="utf-8")
|
||||
usage_reporting_hdr.write_text(contents, encoding="utf-8", newline="\n")
|
||||
|
||||
with (template_directory / "UsageReporting.h.in").open(
|
||||
encoding="utf-8"
|
||||
) as cpp_usage_reporting:
|
||||
contents = (
|
||||
# fmt: off
|
||||
cpp_usage_reporting.read()
|
||||
.replace(r"${usage_reporting_types_cpp}", "\n".join(usage_reporting_types_cpp))
|
||||
.replace(r"${usage_reporting_instances_cpp}", "\n".join(usage_reporting_instances_cpp))
|
||||
# fmt: on
|
||||
)
|
||||
|
||||
usage_reporting_hdr = (
|
||||
output_directory / "main/native/include/hal/UsageReporting.h"
|
||||
)
|
||||
usage_reporting_hdr.write_text(contents, encoding="utf-8", newline="\n")
|
||||
|
||||
|
||||
def main(argv):
|
||||
|
||||
def main():
|
||||
dirname = Path(__file__).parent
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
@@ -85,10 +100,10 @@ def main(argv):
|
||||
default=dirname / "src/generate",
|
||||
type=Path,
|
||||
)
|
||||
args = parser.parse_args(argv)
|
||||
args = parser.parse_args()
|
||||
|
||||
generate_usage_reporting(args.output_directory, args.template_root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
||||
main()
|
||||
|
||||
@@ -4,6 +4,7 @@ kLanguage_Java = 3
|
||||
kLanguage_Python = 4
|
||||
kLanguage_DotNet = 5
|
||||
kLanguage_Kotlin = 6
|
||||
kLanguage_Rust = 7
|
||||
kCANPlugin_BlackJagBridge = 1
|
||||
kCANPlugin_2CAN = 2
|
||||
kFramework_Iterative = 1
|
||||
@@ -13,6 +14,7 @@ kFramework_Timed = 4
|
||||
kFramework_ROS = 5
|
||||
kFramework_RobotBuilder = 6
|
||||
kFramework_AdvantageKit = 7
|
||||
kFramework_MagicBot = 8
|
||||
kRobotDrive_ArcadeStandard = 1
|
||||
kRobotDrive_ArcadeButtonSpin = 2
|
||||
kRobotDrive_ArcadeRatioCurve = 3
|
||||
@@ -26,6 +28,11 @@ kRobotDrive2_MecanumCartesian = 10
|
||||
kRobotDrive2_MecanumPolar = 11
|
||||
kRobotDrive2_KilloughCartesian = 12
|
||||
kRobotDrive2_KilloughPolar = 13
|
||||
kRobotDriveSwerve_Other = 14
|
||||
kRobotDriveSwerve_YAGSL = 15
|
||||
kRobotDriveSwerve_CTRE = 16
|
||||
kRobotDriveSwerve_MaxSwerve = 17
|
||||
kRobotDriveSwerve_AdvantageKit = 18
|
||||
kDriverStationCIO_Analog = 1
|
||||
kDriverStationCIO_DigitalIn = 2
|
||||
kDriverStationCIO_DigitalOut = 3
|
||||
@@ -52,3 +59,12 @@ kKinematics_SwerveDrive = 3
|
||||
kOdometry_DifferentialDrive = 1
|
||||
kOdometry_MecanumDrive = 2
|
||||
kOdometry_SwerveDrive = 3
|
||||
kDashboard_Unknown = 1
|
||||
kDashboard_Glass = 2
|
||||
kDashboard_SmartDashboard = 3
|
||||
kDashboard_Shuffleboard = 4
|
||||
kDashboard_Elastic = 5
|
||||
kDashboard_LabVIEW = 6
|
||||
kDashboard_AdvantageScope = 7
|
||||
kDashboard_QFRCDashboard = 8
|
||||
kDashboard_FRCWebComponents = 9
|
||||
|
||||
@@ -114,3 +114,4 @@ kResourceType_Redux_future4 = 112
|
||||
kResourceType_Redux_future5 = 113
|
||||
kResourceType_RevSparkFlexCAN = 114
|
||||
kResourceType_RevSparkFlexPWM = 115
|
||||
kResourceType_BangBangController = 116
|
||||
|
||||
55
hal/src/generate/UsageReporting.h.in
Normal file
55
hal/src/generate/UsageReporting.h.in
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
#ifndef __UsageReporting_h__
|
||||
#define __UsageReporting_h__
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <stdint.h>
|
||||
#define EXPORT_FUNC __declspec(dllexport) __cdecl
|
||||
#elif defined(__vxworks)
|
||||
#include <vxWorks.h>
|
||||
#define EXPORT_FUNC
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#define EXPORT_FUNC
|
||||
#endif
|
||||
|
||||
#define kUsageReporting_version 1
|
||||
|
||||
namespace nUsageReporting
|
||||
{
|
||||
typedef enum
|
||||
{
|
||||
${usage_reporting_types_cpp}
|
||||
|
||||
// kResourceType_MaximumID = 255,
|
||||
} tResourceType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
${usage_reporting_instances_cpp}
|
||||
} tInstances;
|
||||
|
||||
/**
|
||||
* Report the usage of a resource of interest.
|
||||
*
|
||||
* @param resource one of the values in the tResourceType above (max value 51).
|
||||
* @param instanceNumber an index that identifies the resource instance.
|
||||
* @param context an optional additional context number for some cases (such as module number). Set to 0 to omit.
|
||||
* @param feature a string to be included describing features in use on a specific resource. Setting the same resource more than once allows you to change the feature string.
|
||||
*/
|
||||
uint32_t EXPORT_FUNC report(tResourceType resource, uint8_t instanceNumber, uint8_t context = 0, const char* feature = NULL);
|
||||
} // namespace nUsageReporting
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
uint32_t EXPORT_FUNC FRC_NetworkCommunication_nUsageReporting_report(uint8_t resource, uint8_t instanceNumber, uint8_t context, const char* feature);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __UsageReporting_h__
|
||||
@@ -251,6 +251,8 @@ public final class FRCNetComm {
|
||||
public static final int kResourceType_RevSparkFlexCAN = 114;
|
||||
/** kResourceType_RevSparkFlexPWM = 115. */
|
||||
public static final int kResourceType_RevSparkFlexPWM = 115;
|
||||
/** kResourceType_BangBangController = 116. */
|
||||
public static final int kResourceType_BangBangController = 116;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,6 +275,8 @@ public final class FRCNetComm {
|
||||
public static final int kLanguage_DotNet = 5;
|
||||
/** kLanguage_Kotlin = 6. */
|
||||
public static final int kLanguage_Kotlin = 6;
|
||||
/** kLanguage_Rust = 7. */
|
||||
public static final int kLanguage_Rust = 7;
|
||||
/** kCANPlugin_BlackJagBridge = 1. */
|
||||
public static final int kCANPlugin_BlackJagBridge = 1;
|
||||
/** kCANPlugin_2CAN = 2. */
|
||||
@@ -291,6 +295,8 @@ public final class FRCNetComm {
|
||||
public static final int kFramework_RobotBuilder = 6;
|
||||
/** kFramework_AdvantageKit = 7. */
|
||||
public static final int kFramework_AdvantageKit = 7;
|
||||
/** kFramework_MagicBot = 8. */
|
||||
public static final int kFramework_MagicBot = 8;
|
||||
/** kRobotDrive_ArcadeStandard = 1. */
|
||||
public static final int kRobotDrive_ArcadeStandard = 1;
|
||||
/** kRobotDrive_ArcadeButtonSpin = 2. */
|
||||
@@ -317,6 +323,16 @@ public final class FRCNetComm {
|
||||
public static final int kRobotDrive2_KilloughCartesian = 12;
|
||||
/** kRobotDrive2_KilloughPolar = 13. */
|
||||
public static final int kRobotDrive2_KilloughPolar = 13;
|
||||
/** kRobotDriveSwerve_Other = 14. */
|
||||
public static final int kRobotDriveSwerve_Other = 14;
|
||||
/** kRobotDriveSwerve_YAGSL = 15. */
|
||||
public static final int kRobotDriveSwerve_YAGSL = 15;
|
||||
/** kRobotDriveSwerve_CTRE = 16. */
|
||||
public static final int kRobotDriveSwerve_CTRE = 16;
|
||||
/** kRobotDriveSwerve_MaxSwerve = 17. */
|
||||
public static final int kRobotDriveSwerve_MaxSwerve = 17;
|
||||
/** kRobotDriveSwerve_AdvantageKit = 18. */
|
||||
public static final int kRobotDriveSwerve_AdvantageKit = 18;
|
||||
/** kDriverStationCIO_Analog = 1. */
|
||||
public static final int kDriverStationCIO_Analog = 1;
|
||||
/** kDriverStationCIO_DigitalIn = 2. */
|
||||
@@ -369,6 +385,24 @@ public final class FRCNetComm {
|
||||
public static final int kOdometry_MecanumDrive = 2;
|
||||
/** kOdometry_SwerveDrive = 3. */
|
||||
public static final int kOdometry_SwerveDrive = 3;
|
||||
/** kDashboard_Unknown = 1. */
|
||||
public static final int kDashboard_Unknown = 1;
|
||||
/** kDashboard_Glass = 2. */
|
||||
public static final int kDashboard_Glass = 2;
|
||||
/** kDashboard_SmartDashboard = 3. */
|
||||
public static final int kDashboard_SmartDashboard = 3;
|
||||
/** kDashboard_Shuffleboard = 4. */
|
||||
public static final int kDashboard_Shuffleboard = 4;
|
||||
/** kDashboard_Elastic = 5. */
|
||||
public static final int kDashboard_Elastic = 5;
|
||||
/** kDashboard_LabVIEW = 6. */
|
||||
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_FRCWebComponents = 9. */
|
||||
public static final int kDashboard_FRCWebComponents = 9;
|
||||
}
|
||||
|
||||
/** Utility class. */
|
||||
|
||||
@@ -167,6 +167,7 @@ namespace HALUsageReporting {
|
||||
kResourceType_Redux_future5 = 113,
|
||||
kResourceType_RevSparkFlexCAN = 114,
|
||||
kResourceType_RevSparkFlexPWM = 115,
|
||||
kResourceType_BangBangController = 116,
|
||||
};
|
||||
enum tInstances : int32_t {
|
||||
kLanguage_LabVIEW = 1,
|
||||
@@ -175,6 +176,7 @@ namespace HALUsageReporting {
|
||||
kLanguage_Python = 4,
|
||||
kLanguage_DotNet = 5,
|
||||
kLanguage_Kotlin = 6,
|
||||
kLanguage_Rust = 7,
|
||||
kCANPlugin_BlackJagBridge = 1,
|
||||
kCANPlugin_2CAN = 2,
|
||||
kFramework_Iterative = 1,
|
||||
@@ -184,6 +186,7 @@ namespace HALUsageReporting {
|
||||
kFramework_ROS = 5,
|
||||
kFramework_RobotBuilder = 6,
|
||||
kFramework_AdvantageKit = 7,
|
||||
kFramework_MagicBot = 8,
|
||||
kRobotDrive_ArcadeStandard = 1,
|
||||
kRobotDrive_ArcadeButtonSpin = 2,
|
||||
kRobotDrive_ArcadeRatioCurve = 3,
|
||||
@@ -197,6 +200,11 @@ namespace HALUsageReporting {
|
||||
kRobotDrive2_MecanumPolar = 11,
|
||||
kRobotDrive2_KilloughCartesian = 12,
|
||||
kRobotDrive2_KilloughPolar = 13,
|
||||
kRobotDriveSwerve_Other = 14,
|
||||
kRobotDriveSwerve_YAGSL = 15,
|
||||
kRobotDriveSwerve_CTRE = 16,
|
||||
kRobotDriveSwerve_MaxSwerve = 17,
|
||||
kRobotDriveSwerve_AdvantageKit = 18,
|
||||
kDriverStationCIO_Analog = 1,
|
||||
kDriverStationCIO_DigitalIn = 2,
|
||||
kDriverStationCIO_DigitalOut = 3,
|
||||
@@ -223,6 +231,15 @@ namespace HALUsageReporting {
|
||||
kOdometry_DifferentialDrive = 1,
|
||||
kOdometry_MecanumDrive = 2,
|
||||
kOdometry_SwerveDrive = 3,
|
||||
kDashboard_Unknown = 1,
|
||||
kDashboard_Glass = 2,
|
||||
kDashboard_SmartDashboard = 3,
|
||||
kDashboard_Shuffleboard = 4,
|
||||
kDashboard_Elastic = 5,
|
||||
kDashboard_LabVIEW = 6,
|
||||
kDashboard_AdvantageScope = 7,
|
||||
kDashboard_QFRCDashboard = 8,
|
||||
kDashboard_FRCWebComponents = 9,
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
240
hal/src/generated/main/native/include/hal/UsageReporting.h
generated
Normal file
240
hal/src/generated/main/native/include/hal/UsageReporting.h
generated
Normal file
@@ -0,0 +1,240 @@
|
||||
|
||||
#ifndef __UsageReporting_h__
|
||||
#define __UsageReporting_h__
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <stdint.h>
|
||||
#define EXPORT_FUNC __declspec(dllexport) __cdecl
|
||||
#elif defined(__vxworks)
|
||||
#include <vxWorks.h>
|
||||
#define EXPORT_FUNC
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#define EXPORT_FUNC
|
||||
#endif
|
||||
|
||||
#define kUsageReporting_version 1
|
||||
|
||||
namespace nUsageReporting
|
||||
{
|
||||
typedef enum
|
||||
{
|
||||
kResourceType_Controller = 0,
|
||||
kResourceType_Module = 1,
|
||||
kResourceType_Language = 2,
|
||||
kResourceType_CANPlugin = 3,
|
||||
kResourceType_Accelerometer = 4,
|
||||
kResourceType_ADXL345 = 5,
|
||||
kResourceType_AnalogChannel = 6,
|
||||
kResourceType_AnalogTrigger = 7,
|
||||
kResourceType_AnalogTriggerOutput = 8,
|
||||
kResourceType_CANJaguar = 9,
|
||||
kResourceType_Compressor = 10,
|
||||
kResourceType_Counter = 11,
|
||||
kResourceType_Dashboard = 12,
|
||||
kResourceType_DigitalInput = 13,
|
||||
kResourceType_DigitalOutput = 14,
|
||||
kResourceType_DriverStationCIO = 15,
|
||||
kResourceType_DriverStationEIO = 16,
|
||||
kResourceType_DriverStationLCD = 17,
|
||||
kResourceType_Encoder = 18,
|
||||
kResourceType_GearTooth = 19,
|
||||
kResourceType_Gyro = 20,
|
||||
kResourceType_I2C = 21,
|
||||
kResourceType_Framework = 22,
|
||||
kResourceType_Jaguar = 23,
|
||||
kResourceType_Joystick = 24,
|
||||
kResourceType_Kinect = 25,
|
||||
kResourceType_KinectStick = 26,
|
||||
kResourceType_PIDController = 27,
|
||||
kResourceType_Preferences = 28,
|
||||
kResourceType_PWM = 29,
|
||||
kResourceType_Relay = 30,
|
||||
kResourceType_RobotDrive = 31,
|
||||
kResourceType_SerialPort = 32,
|
||||
kResourceType_Servo = 33,
|
||||
kResourceType_Solenoid = 34,
|
||||
kResourceType_SPI = 35,
|
||||
kResourceType_Task = 36,
|
||||
kResourceType_Ultrasonic = 37,
|
||||
kResourceType_Victor = 38,
|
||||
kResourceType_Button = 39,
|
||||
kResourceType_Command = 40,
|
||||
kResourceType_AxisCamera = 41,
|
||||
kResourceType_PCVideoServer = 42,
|
||||
kResourceType_SmartDashboard = 43,
|
||||
kResourceType_Talon = 44,
|
||||
kResourceType_HiTechnicColorSensor = 45,
|
||||
kResourceType_HiTechnicAccel = 46,
|
||||
kResourceType_HiTechnicCompass = 47,
|
||||
kResourceType_SRF08 = 48,
|
||||
kResourceType_AnalogOutput = 49,
|
||||
kResourceType_VictorSP = 50,
|
||||
kResourceType_PWMTalonSRX = 51,
|
||||
kResourceType_CANTalonSRX = 52,
|
||||
kResourceType_ADXL362 = 53,
|
||||
kResourceType_ADXRS450 = 54,
|
||||
kResourceType_RevSPARK = 55,
|
||||
kResourceType_MindsensorsSD540 = 56,
|
||||
kResourceType_DigitalGlitchFilter = 57,
|
||||
kResourceType_ADIS16448 = 58,
|
||||
kResourceType_PDP = 59,
|
||||
kResourceType_PCM = 60,
|
||||
kResourceType_PigeonIMU = 61,
|
||||
kResourceType_NidecBrushless = 62,
|
||||
kResourceType_CANifier = 63,
|
||||
kResourceType_TalonFX = 64,
|
||||
kResourceType_CTRE_future1 = 65,
|
||||
kResourceType_CTRE_future2 = 66,
|
||||
kResourceType_CTRE_future3 = 67,
|
||||
kResourceType_CTRE_future4 = 68,
|
||||
kResourceType_CTRE_future5 = 69,
|
||||
kResourceType_CTRE_future6 = 70,
|
||||
kResourceType_LinearFilter = 71,
|
||||
kResourceType_XboxController = 72,
|
||||
kResourceType_UsbCamera = 73,
|
||||
kResourceType_NavX = 74,
|
||||
kResourceType_Pixy = 75,
|
||||
kResourceType_Pixy2 = 76,
|
||||
kResourceType_ScanseSweep = 77,
|
||||
kResourceType_Shuffleboard = 78,
|
||||
kResourceType_CAN = 79,
|
||||
kResourceType_DigilentDMC60 = 80,
|
||||
kResourceType_PWMVictorSPX = 81,
|
||||
kResourceType_RevSparkMaxPWM = 82,
|
||||
kResourceType_RevSparkMaxCAN = 83,
|
||||
kResourceType_ADIS16470 = 84,
|
||||
kResourceType_PIDController2 = 85,
|
||||
kResourceType_ProfiledPIDController = 86,
|
||||
kResourceType_Kinematics = 87,
|
||||
kResourceType_Odometry = 88,
|
||||
kResourceType_Units = 89,
|
||||
kResourceType_TrapezoidProfile = 90,
|
||||
kResourceType_DutyCycle = 91,
|
||||
kResourceType_AddressableLEDs = 92,
|
||||
kResourceType_FusionVenom = 93,
|
||||
kResourceType_CTRE_future7 = 94,
|
||||
kResourceType_CTRE_future8 = 95,
|
||||
kResourceType_CTRE_future9 = 96,
|
||||
kResourceType_CTRE_future10 = 97,
|
||||
kResourceType_CTRE_future11 = 98,
|
||||
kResourceType_CTRE_future12 = 99,
|
||||
kResourceType_CTRE_future13 = 100,
|
||||
kResourceType_CTRE_future14 = 101,
|
||||
kResourceType_ExponentialProfile = 102,
|
||||
kResourceType_PS4Controller = 103,
|
||||
kResourceType_PhotonCamera = 104,
|
||||
kResourceType_PhotonPoseEstimator = 105,
|
||||
kResourceType_PathPlannerPath = 106,
|
||||
kResourceType_PathPlannerAuto = 107,
|
||||
kResourceType_PathFindingCommand = 108,
|
||||
kResourceType_Redux_future1 = 109,
|
||||
kResourceType_Redux_future2 = 110,
|
||||
kResourceType_Redux_future3 = 111,
|
||||
kResourceType_Redux_future4 = 112,
|
||||
kResourceType_Redux_future5 = 113,
|
||||
kResourceType_RevSparkFlexCAN = 114,
|
||||
kResourceType_RevSparkFlexPWM = 115,
|
||||
kResourceType_BangBangController = 116,
|
||||
|
||||
// kResourceType_MaximumID = 255,
|
||||
} tResourceType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
kLanguage_LabVIEW = 1,
|
||||
kLanguage_CPlusPlus = 2,
|
||||
kLanguage_Java = 3,
|
||||
kLanguage_Python = 4,
|
||||
kLanguage_DotNet = 5,
|
||||
kLanguage_Kotlin = 6,
|
||||
kLanguage_Rust = 7,
|
||||
kCANPlugin_BlackJagBridge = 1,
|
||||
kCANPlugin_2CAN = 2,
|
||||
kFramework_Iterative = 1,
|
||||
kFramework_Simple = 2,
|
||||
kFramework_CommandControl = 3,
|
||||
kFramework_Timed = 4,
|
||||
kFramework_ROS = 5,
|
||||
kFramework_RobotBuilder = 6,
|
||||
kFramework_AdvantageKit = 7,
|
||||
kFramework_MagicBot = 8,
|
||||
kRobotDrive_ArcadeStandard = 1,
|
||||
kRobotDrive_ArcadeButtonSpin = 2,
|
||||
kRobotDrive_ArcadeRatioCurve = 3,
|
||||
kRobotDrive_Tank = 4,
|
||||
kRobotDrive_MecanumPolar = 5,
|
||||
kRobotDrive_MecanumCartesian = 6,
|
||||
kRobotDrive2_DifferentialArcade = 7,
|
||||
kRobotDrive2_DifferentialTank = 8,
|
||||
kRobotDrive2_DifferentialCurvature = 9,
|
||||
kRobotDrive2_MecanumCartesian = 10,
|
||||
kRobotDrive2_MecanumPolar = 11,
|
||||
kRobotDrive2_KilloughCartesian = 12,
|
||||
kRobotDrive2_KilloughPolar = 13,
|
||||
kRobotDriveSwerve_Other = 14,
|
||||
kRobotDriveSwerve_YAGSL = 15,
|
||||
kRobotDriveSwerve_CTRE = 16,
|
||||
kRobotDriveSwerve_MaxSwerve = 17,
|
||||
kRobotDriveSwerve_AdvantageKit = 18,
|
||||
kDriverStationCIO_Analog = 1,
|
||||
kDriverStationCIO_DigitalIn = 2,
|
||||
kDriverStationCIO_DigitalOut = 3,
|
||||
kDriverStationEIO_Acceleration = 1,
|
||||
kDriverStationEIO_AnalogIn = 2,
|
||||
kDriverStationEIO_AnalogOut = 3,
|
||||
kDriverStationEIO_Button = 4,
|
||||
kDriverStationEIO_LED = 5,
|
||||
kDriverStationEIO_DigitalIn = 6,
|
||||
kDriverStationEIO_DigitalOut = 7,
|
||||
kDriverStationEIO_FixedDigitalOut = 8,
|
||||
kDriverStationEIO_PWM = 9,
|
||||
kDriverStationEIO_Encoder = 10,
|
||||
kDriverStationEIO_TouchSlider = 11,
|
||||
kADXL345_SPI = 1,
|
||||
kADXL345_I2C = 2,
|
||||
kCommand_Scheduler = 1,
|
||||
kCommand2_Scheduler = 2,
|
||||
kSmartDashboard_Instance = 1,
|
||||
kSmartDashboard_LiveWindow = 2,
|
||||
kKinematics_DifferentialDrive = 1,
|
||||
kKinematics_MecanumDrive = 2,
|
||||
kKinematics_SwerveDrive = 3,
|
||||
kOdometry_DifferentialDrive = 1,
|
||||
kOdometry_MecanumDrive = 2,
|
||||
kOdometry_SwerveDrive = 3,
|
||||
kDashboard_Unknown = 1,
|
||||
kDashboard_Glass = 2,
|
||||
kDashboard_SmartDashboard = 3,
|
||||
kDashboard_Shuffleboard = 4,
|
||||
kDashboard_Elastic = 5,
|
||||
kDashboard_LabVIEW = 6,
|
||||
kDashboard_AdvantageScope = 7,
|
||||
kDashboard_QFRCDashboard = 8,
|
||||
kDashboard_FRCWebComponents = 9,
|
||||
} tInstances;
|
||||
|
||||
/**
|
||||
* Report the usage of a resource of interest.
|
||||
*
|
||||
* @param resource one of the values in the tResourceType above (max value 51).
|
||||
* @param instanceNumber an index that identifies the resource instance.
|
||||
* @param context an optional additional context number for some cases (such as module number). Set to 0 to omit.
|
||||
* @param feature a string to be included describing features in use on a specific resource. Setting the same resource more than once allows you to change the feature string.
|
||||
*/
|
||||
uint32_t EXPORT_FUNC report(tResourceType resource, uint8_t instanceNumber, uint8_t context = 0, const char* feature = NULL);
|
||||
} // namespace nUsageReporting
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
uint32_t EXPORT_FUNC FRC_NetworkCommunication_nUsageReporting_report(uint8_t resource, uint8_t instanceNumber, uint8_t context, const char* feature);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __UsageReporting_h__
|
||||
@@ -19,16 +19,4 @@ HAL_ENUM(HAL_I2CPort) {
|
||||
HAL_I2C_kOnboard,
|
||||
HAL_I2C_kMXP
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace hal {
|
||||
|
||||
/**
|
||||
* A move-only C++ wrapper around HAL_I2CPort.
|
||||
* Does not ensure destruction.
|
||||
*/
|
||||
using I2CPort = Handle<HAL_I2CPort, nullptr, HAL_I2C_kInvalid>;
|
||||
|
||||
} // namespace hal
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
@@ -41,16 +41,4 @@ HAL_ENUM(HAL_SPIMode) {
|
||||
/** Clock idle high, data sampled on rising edge. */
|
||||
HAL_SPI_kMode3 = 3,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace hal {
|
||||
|
||||
/**
|
||||
* A move-only C++ wrapper around HAL_SPIPort.
|
||||
* Does not ensure destruction.
|
||||
*/
|
||||
using SPIPort = Handle<HAL_SPIPort, nullptr, HAL_SPI_kInvalid>;
|
||||
|
||||
} // namespace hal
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
@@ -55,6 +55,7 @@ struct FRCDriverStation {
|
||||
~FRCDriverStation() { gShutdown = true; }
|
||||
wpi::EventVector newDataEvents;
|
||||
wpi::mutex cacheMutex;
|
||||
wpi::mutex tcpCacheMutex;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -90,6 +91,29 @@ static std::atomic<JoystickDataCache*> currentCache{nullptr};
|
||||
static JoystickDataCache* lastGiven = &caches[1];
|
||||
static JoystickDataCache* cacheToUpdate = &caches[2];
|
||||
|
||||
namespace {
|
||||
struct TcpCache {
|
||||
TcpCache() { std::memset(this, 0, sizeof(*this)); }
|
||||
void Update();
|
||||
void CloneTo(TcpCache* other) { std::memcpy(other, this, sizeof(*this)); }
|
||||
|
||||
HAL_MatchInfo matchInfo;
|
||||
HAL_JoystickDescriptor descriptors[HAL_kMaxJoysticks];
|
||||
};
|
||||
static_assert(std::is_standard_layout_v<TcpCache>);
|
||||
} // namespace
|
||||
|
||||
static TcpCache tcpCache;
|
||||
static TcpCache tcpCurrent;
|
||||
|
||||
void TcpCache::Update() {
|
||||
SimDriverStationData->GetMatchInfo(&matchInfo);
|
||||
|
||||
for (int i = 0; i < HAL_kMaxJoysticks; i++) {
|
||||
SimDriverStationData->GetJoystickDescriptor(i, &descriptors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static ::FRCDriverStation* driverStation;
|
||||
|
||||
namespace hal::init {
|
||||
@@ -256,32 +280,47 @@ void HAL_GetAllJoystickData(HAL_JoystickAxes* axes, HAL_JoystickPOVs* povs,
|
||||
int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
|
||||
HAL_JoystickDescriptor* desc) {
|
||||
CHECK_JOYSTICK_NUMBER(joystickNum);
|
||||
SimDriverStationData->GetJoystickDescriptor(joystickNum, desc);
|
||||
std::scoped_lock lock{driverStation->tcpCacheMutex};
|
||||
*desc = tcpCurrent.descriptors[joystickNum];
|
||||
return 0;
|
||||
}
|
||||
|
||||
HAL_Bool HAL_GetJoystickIsXbox(int32_t joystickNum) {
|
||||
HAL_JoystickDescriptor desc;
|
||||
SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
|
||||
return desc.isXbox;
|
||||
HAL_JoystickDescriptor joystickDesc;
|
||||
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return joystickDesc.isXbox;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t HAL_GetJoystickType(int32_t joystickNum) {
|
||||
HAL_JoystickDescriptor desc;
|
||||
SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
|
||||
return desc.type;
|
||||
HAL_JoystickDescriptor joystickDesc;
|
||||
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return joystickDesc.type;
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_GetJoystickName(struct WPI_String* name, int32_t joystickNum) {
|
||||
HAL_JoystickDescriptor desc;
|
||||
SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
|
||||
size_t len = std::strlen(desc.name);
|
||||
HAL_JoystickDescriptor joystickDesc;
|
||||
const char* cName = joystickDesc.name;
|
||||
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
|
||||
cName = "";
|
||||
}
|
||||
auto len = std::strlen(cName);
|
||||
auto write = WPI_AllocateString(name, len);
|
||||
std::memcpy(write, desc.name, len);
|
||||
std::memcpy(write, cName, len);
|
||||
}
|
||||
|
||||
int32_t HAL_GetJoystickAxisType(int32_t joystickNum, int32_t axis) {
|
||||
return 0;
|
||||
HAL_JoystickDescriptor joystickDesc;
|
||||
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return joystickDesc.axisTypes[axis];
|
||||
}
|
||||
}
|
||||
|
||||
int32_t HAL_SetJoystickOutputs(int32_t joystickNum, int64_t outputs,
|
||||
@@ -300,7 +339,8 @@ double HAL_GetMatchTime(int32_t* status) {
|
||||
}
|
||||
|
||||
int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
|
||||
SimDriverStationData->GetMatchInfo(info);
|
||||
std::scoped_lock lock{driverStation->tcpCacheMutex};
|
||||
*info = tcpCurrent.matchInfo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -329,31 +369,42 @@ HAL_Bool HAL_RefreshDSData(void) {
|
||||
return false;
|
||||
}
|
||||
bool dsAttached = SimDriverStationData->dsAttached;
|
||||
std::scoped_lock lock{driverStation->cacheMutex};
|
||||
JoystickDataCache* prev = currentCache.exchange(nullptr);
|
||||
if (prev != nullptr) {
|
||||
currentRead = prev;
|
||||
JoystickDataCache* prev;
|
||||
{
|
||||
std::scoped_lock lock{driverStation->cacheMutex};
|
||||
prev = currentCache.exchange(nullptr);
|
||||
if (prev != nullptr) {
|
||||
currentRead = prev;
|
||||
}
|
||||
// If newest state shows we have a DS attached, just use the
|
||||
// control word out of the cache, As it will be the one in sync
|
||||
// with the data. If no data has been updated, at this point,
|
||||
// and a DS wasn't attached previously, this will still return
|
||||
// a zeroed out control word, with is the correct state for
|
||||
// no new data.
|
||||
if (!dsAttached) {
|
||||
// If the DS is not attached, we need to zero out the control word.
|
||||
// This is because HAL_RefreshDSData is called asynchronously from
|
||||
// the DS data. The dsAttached variable comes directly from netcomm
|
||||
// and could be updated before the caches are. If that happens,
|
||||
// we would end up returning the previous cached control word,
|
||||
// which is out of sync with the current control word and could
|
||||
// break invariants such as which alliance station is in used.
|
||||
// Also, when the DS has never been connected the rest of the fields
|
||||
// in control word are garbage, so we also need to zero out in that
|
||||
// case too
|
||||
std::memset(¤tRead->controlWord, 0,
|
||||
sizeof(currentRead->controlWord));
|
||||
}
|
||||
newestControlWord = currentRead->controlWord;
|
||||
}
|
||||
// If newest state shows we have a DS attached, just use the
|
||||
// control word out of the cache, As it will be the one in sync
|
||||
// with the data. If no data has been updated, at this point,
|
||||
// and a DS wasn't attached previously, this will still return
|
||||
// a zeroed out control word, with is the correct state for
|
||||
// no new data.
|
||||
if (!dsAttached) {
|
||||
// If the DS is not attached, we need to zero out the control word.
|
||||
// This is because HAL_RefreshDSData is called asynchronously from
|
||||
// the DS data. The dsAttached variable comes directly from netcomm
|
||||
// and could be updated before the caches are. If that happens,
|
||||
// we would end up returning the previous cached control word,
|
||||
// which is out of sync with the current control word and could
|
||||
// break invariants such as which alliance station is in used.
|
||||
// Also, when the DS has never been connected the rest of the fields
|
||||
// in control word are garbage, so we also need to zero out in that
|
||||
// case too
|
||||
std::memset(¤tRead->controlWord, 0, sizeof(currentRead->controlWord));
|
||||
|
||||
{
|
||||
tcpCache.Update();
|
||||
std::scoped_lock tcpLock(driverStation->tcpCacheMutex);
|
||||
tcpCache.CloneTo(&tcpCurrent);
|
||||
}
|
||||
newestControlWord = currentRead->controlWord;
|
||||
|
||||
return prev != nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -317,7 +317,7 @@ HAL_SimDeviceHandle SimDeviceData::GetDeviceHandle(const char* name) {
|
||||
if (it == m_deviceMap.end()) {
|
||||
return 0;
|
||||
}
|
||||
if (auto deviceImpl = it->getValue().lock()) {
|
||||
if (auto deviceImpl = it->second.lock()) {
|
||||
return deviceImpl->handle;
|
||||
} else {
|
||||
return 0;
|
||||
@@ -459,10 +459,10 @@ HAL_SimValueHandle SimDeviceData::GetValueHandle(HAL_SimDeviceHandle device,
|
||||
if (it == deviceImpl->valueMap.end()) {
|
||||
return 0;
|
||||
}
|
||||
if (!it->getValue()) {
|
||||
if (!it->second) {
|
||||
return 0;
|
||||
}
|
||||
return it->getValue()->handle;
|
||||
return it->second->handle;
|
||||
}
|
||||
|
||||
void SimDeviceData::EnumerateValues(HAL_SimDeviceHandle device, void* param,
|
||||
|
||||
@@ -131,6 +131,8 @@ TEST(DriverStationTest, EventInfo) {
|
||||
info.replayNumber = 42;
|
||||
HALSIM_SetMatchInfo(&info);
|
||||
|
||||
HAL_RefreshDSData();
|
||||
|
||||
HAL_MatchInfo dataBack;
|
||||
HAL_GetMatchInfo(&dataBack);
|
||||
|
||||
|
||||
92
ntcore/BUILD.bazel
Normal file
92
ntcore/BUILD.bazel
Normal file
@@ -0,0 +1,92 @@
|
||||
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
|
||||
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
|
||||
|
||||
cc_library(
|
||||
name = "generated_cc_headers",
|
||||
hdrs = glob(["src/generated/main/native/include/**"]),
|
||||
includes = ["src/generated/main/native/include"],
|
||||
strip_include_prefix = "src/generated/main/native/include",
|
||||
visibility = ["//ntcore:__subpackages__"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "generated_cc_source",
|
||||
srcs = glob(
|
||||
["src/generated/main/native/cpp/**"],
|
||||
exclude = ["src/generated/main/native/cpp/jni/**"],
|
||||
),
|
||||
visibility = ["//ntcore:__subpackages__"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "generated_java",
|
||||
srcs = glob(["src/generated/main/java/**/*.java"]),
|
||||
visibility = ["//ntcore:__subpackages__"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "ntcore.static",
|
||||
srcs = glob(
|
||||
["src/main/native/cpp/**"],
|
||||
exclude = ["src/main/native/cpp/jni/**"],
|
||||
) + [":generated_cc_source"],
|
||||
hdrs = glob(["src/main/native/include/**/*"]),
|
||||
includes = [
|
||||
"src/main/native/cpp",
|
||||
"src/main/native/include",
|
||||
],
|
||||
strip_include_prefix = "src/main/native/include",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":generated_cc_headers",
|
||||
"//wpinet:wpinet.static",
|
||||
"//wpiutil:wpiutil.static",
|
||||
],
|
||||
)
|
||||
|
||||
java_library(
|
||||
name = "networktables-java",
|
||||
srcs = glob(["src/main/java/**/*.java"]) + [":generated_java"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//wpiutil:wpiutil-java",
|
||||
"@maven//:us_hebi_quickbuf_quickbuf_runtime",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "ntcore-cpp-test",
|
||||
size = "small",
|
||||
srcs = glob([
|
||||
"src/test/native/**/*.cpp",
|
||||
"src/test/native/**/*.h",
|
||||
]),
|
||||
tags = [
|
||||
"exclusive",
|
||||
"no-asan",
|
||||
"no-tsan",
|
||||
],
|
||||
deps = [
|
||||
":ntcore.static",
|
||||
"//thirdparty/googletest:googletest.static",
|
||||
"//wpiutil:wpiutil-testlib",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "DevMain-Cpp",
|
||||
srcs = ["src/dev/native/cpp/main.cpp"],
|
||||
deps = [
|
||||
":ntcore.static",
|
||||
],
|
||||
)
|
||||
|
||||
java_binary(
|
||||
name = "DevMain-Java",
|
||||
srcs = ["src/dev/java/edu/wpi/first/ntcore/DevMain.java"],
|
||||
main_class = "edu.wpi.first.ntcore.DevMain",
|
||||
deps = [
|
||||
"networktables-java",
|
||||
"//wpiutil:wpiutil-java",
|
||||
],
|
||||
)
|
||||
@@ -7,9 +7,11 @@ file(
|
||||
GLOB ntcore_native_src
|
||||
src/main/native/cpp/*.cpp
|
||||
src/generated/main/native/cpp/*.cpp
|
||||
src/main/native/cpp/local/*.cpp
|
||||
src/main/native/cpp/net/*.cpp
|
||||
src/main/native/cpp/net3/*.cpp
|
||||
src/main/native/cpp/networktables/*.cpp
|
||||
src/main/native/cpp/server/*.cpp
|
||||
src/main/native/cpp/tables/*.cpp
|
||||
)
|
||||
add_library(ntcore ${ntcore_native_src})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user