Merge branch 'main' into 2027

This commit is contained in:
Peter Johnson
2024-12-19 20:40:37 -08:00
46 changed files with 1512 additions and 431 deletions

61
.github/actions/pregen/action.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: 'Setup and run pregeneration'
description: 'Sets up the dependencies needed to generate generated files and runs all generation scripts'
runs:
using: "composite"
steps:
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install jinja and protobuf
run: python -m pip install jinja2 protobuf grpcio-tools
shell: bash
- 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
shell: bash
- name: Regenerate hal
run: ./hal/generate_usage_reporting.py
shell: bash
- name: Regenerate ntcore
run: ./ntcore/generate_topics.py
shell: bash
- name: Regenerate imgui
run: |
./thirdparty/imgui_suite/generate_fonts.sh
./thirdparty/imgui_suite/generate_gl3w.py
shell: bash
- name: Regenerate HIDs
run: |
./wpilibc/generate_hids.py
./wpilibj/generate_hids.py
./wpilibNewCommands/generate_hids.py
shell: bash
- name: Regenerate PWM motor controllers
run: |
./wpilibc/generate_pwm_motor_controllers.py
./wpilibj/generate_pwm_motor_controllers.py
shell: bash
- name: Regenerate wpimath
run: |
./wpimath/generate_nanopb.py
./wpimath/generate_numbers.py
./wpimath/generate_quickbuf.py --quickbuf_plugin protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
shell: bash
- name: Regenerate wpiunits
run: ./wpiunits/generate_units.py
shell: bash
- name: Regenerate wpiutil nanopb
run: ./wpiutil/generate_nanopb.py
shell: bash

View File

@@ -1,103 +0,0 @@
name: Comment Commands
on:
issue_comment:
types: [ created ]
jobs:
format:
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/format')
runs-on: ubuntu-24.04
steps:
- name: React Rocket
uses: actions/github-script@v7
with:
script: |
const {owner, repo} = context.issue
github.rest.reactions.createForIssueComment({
owner,
repo,
comment_id: context.payload.comment.id,
content: "rocket",
});
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}
- name: Fetch all history and metadata
run: |
git checkout -b pr
git branch -f main origin/main
- name: Checkout PR
run: |
gh pr checkout $NUMBER
env:
GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}"
NUMBER: ${{ github.event.issue.number }}
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Install wpiformat
run: |
python -m venv ${{ runner.temp }}/wpiformat
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2024.50
- name: Run wpiformat
run: ${{ runner.temp }}/wpiformat/bin/wpiformat
- name: Run spotlessApply
run: ./gradlew spotlessApply
- name: Commit
run: |
# Set credentials
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Commit
git commit -am "Formatting fixes"
git push
pregen:
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/pregen')
runs-on: ubuntu-24.04
steps:
- name: React Rocket
uses: actions/github-script@v7
with:
script: |
const {owner, repo} = context.issue
github.rest.reactions.createForIssueComment({
owner,
repo,
comment_id: context.payload.comment.id,
content: "rocket",
});
- uses: actions/checkout@v4
with:
token: ${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}
- name: Checkout PR
run: |
gh pr checkout $NUMBER
env:
GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}"
NUMBER: ${{ github.event.issue.number }}
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install jinja
run: python -m pip install jinja2
- 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
run: ./.github/workflows/pregen_all.py --quickbuf_plugin=protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
- name: Commit
run: |
# Set credentials
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Commit
git commit -am "Regenerate pregenerated files"
git push

View File

@@ -1,85 +0,0 @@
#!/usr/bin/env python3
import argparse
import subprocess
import sys
from pathlib import Path
def main():
script_path = Path(__file__).resolve()
REPO_ROOT = script_path.parent.parent.parent
parser = argparse.ArgumentParser()
parser.add_argument(
"--quickbuf_plugin",
help="Path to the quickbuf protoc plugin",
required=True,
)
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}/hal/generate_nanopb.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(
[
sys.executable,
f"{REPO_ROOT}/wpimath/generate_quickbuf.py",
f"--quickbuf_plugin={args.quickbuf_plugin}",
],
check=True,
)
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()

View File

@@ -18,16 +18,8 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
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
run: python ./.github/workflows/pregen_all.py --quickbuf_plugin protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
- name: Run pregen
uses: ./.github/actions/pregen
- name: Add untracked files to index so they count as changes
run: git add -A
- name: Check output

View File

@@ -35,8 +35,8 @@ maven_install(
# 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",
sha256 = "fe267e2af53c1def1e962700a9aeda9e8fdfa9fb46b72167c615ec0e25447dd6",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_toolchains/releases/download/2025-1/rules_bzlmodRio_toolchains-2025-1.tar.gz",
)
load("@rules_bzlmodrio_toolchains//:maven_deps.bzl", "setup_legacy_setup_toolchains_dependencies")
@@ -71,6 +71,12 @@ register_toolchains(
"@local_bullseye_64//:macos",
"@local_bullseye_64//:linux",
"@local_bullseye_64//:windows",
"@local_bookworm_32//:macos",
"@local_bookworm_32//:linux",
"@local_bookworm_32//:windows",
"@local_bookworm_64//:macos",
"@local_bookworm_64//:linux",
"@local_bookworm_64//:windows",
)
setup_legacy_setup_jdk_dependencies()
@@ -87,8 +93,8 @@ 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",
sha256 = "4f4a607956ca8555618736c3058dd96e09d02df19e95088c1e352d2319fd70c7",
url = "https://github.com/wpilibsuite/bzlmodRio-opencv/releases/download/2025.4.10.0-2/bzlmodRio-opencv-2025.4.10.0-2.tar.gz",
)
load("@bzlmodrio-opencv//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_opencv_cpp_dependencies")
@@ -98,3 +104,16 @@ 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()
http_archive(
name = "build_bazel_apple_support",
sha256 = "c4bb2b7367c484382300aee75be598b92f847896fb31bbd22f3a2346adf66a80",
url = "https://github.com/bazelbuild/apple_support/releases/download/1.15.1/apple_support.1.15.1.tar.gz",
)
load(
"@build_bazel_apple_support//lib:repositories.bzl",
"apple_support_dependencies",
)
apple_support_dependencies()

112
apriltag/BUILD.bazel Normal file
View File

@@ -0,0 +1,112 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
load("@rules_python//python:defs.bzl", "py_binary")
load("//shared/bazel/rules/gen:gen-resources.bzl", "generate_resources")
cc_library(
name = "thirdparty-apriltag",
srcs = glob(["src/main/native/thirdparty/apriltag/src/**"]),
hdrs = glob(["src/main/native/thirdparty/apriltag/include/**"]),
copts = select({
"@bazel_tools//src/conditions:darwin": [
"-Wno-format-nonliteral",
"-Wno-gnu-zero-variadic-macro-arguments",
"-Wno-uninitialized",
"-Wno-sign-compare",
"-Wno-type-limits",
],
"@bazel_tools//src/conditions:windows": [
"/wd4005",
"/wd4018",
"/wd4244",
"/wd4267",
"/wd4996",
],
"@rules_bzlmodrio_toolchains//constraints/combined:is_linux": [
"-Wno-format-nonliteral",
"-Wno-maybe-uninitialized",
"-Wno-sign-compare",
"-Wno-type-limits",
],
}),
includes = ["src/main/native/thirdparty/apriltag/include/common"],
strip_include_prefix = "src/main/native/thirdparty/apriltag/include",
visibility = ["//visibility:public"],
)
generate_resources(
name = "generate-resources",
namespace = "frc",
prefix = "APRILTAG",
resource_files = glob(["src/main/native/resources/**"]),
visibility = ["//visibility:public"],
)
cc_library(
name = "apriltag.static",
srcs = [":generate-resources"] + glob(
["src/main/native/cpp/**"],
exclude = ["src/main/native/cpp/jni/**"],
),
hdrs = glob(["src/main/native/include/**/*"]),
defines = ["WPILIB_EXPORTS"],
strip_include_prefix = "src/main/native/include",
visibility = ["//visibility:public"],
deps = [
":thirdparty-apriltag",
"//wpimath:wpimath.static",
"//wpiutil:wpiutil.static",
],
)
java_library(
name = "apriltag-java",
srcs = glob(["src/main/java/**/*.java"]),
resource_strip_prefix = "apriltag/src/main/native/resources",
resources = glob(["src/main/native/resources/**"]),
visibility = ["//visibility:public"],
deps = [
"//wpimath:wpimath-java",
"//wpiutil:wpiutil-java",
"@bzlmodrio-opencv//libraries/java/opencv",
"@maven//:com_fasterxml_jackson_core_jackson_annotations",
"@maven//:com_fasterxml_jackson_core_jackson_core",
"@maven//:com_fasterxml_jackson_core_jackson_databind",
],
)
cc_test(
name = "apriltag-cpp-test",
size = "small",
srcs = glob(["src/test/native/cpp/**"]),
tags = [
"no-asan",
],
deps = [
":apriltag.static",
"//thirdparty/googletest:googletest.static",
],
)
cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
deps = [
":apriltag.static",
],
)
java_binary(
name = "DevMain-Java",
srcs = ["src/dev/java/edu/wpi/first/apriltag/DevMain.java"],
main_class = "edu.wpi.first.apriltag.DevMain",
deps = [
":apriltag-java",
],
)
py_binary(
name = "convert_apriltag_layouts",
srcs = ["convert_apriltag_layouts.py"],
tags = ["manual"],
)

View File

@@ -1,6 +1,22 @@
load("@rules_cc//cc:defs.bzl", "cc_binary")
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 = "cameraserver.static",
srcs = glob(["src/main/native/cpp/**"]),
hdrs = glob(["src/main/native/include/**/*"]),
includes = [
"cpp",
"src/main/native/include",
],
strip_include_prefix = "src/main/native/include",
visibility = ["//visibility:public"],
deps = [
"//cscore:cscore.static",
"//ntcore:ntcore.static",
],
)
java_library(
name = "cameraserver-java",
srcs = glob(["src/main/java/**/*.java"]),
@@ -16,10 +32,21 @@ java_library(
],
)
cc_test(
name = "cameraserver-cpp-test",
size = "small",
srcs = glob(["src/test/native/**"]),
deps = [
":cameraserver.static",
"//thirdparty/googletest:googletest.static",
],
)
cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
deps = [
":cameraserver.static",
],
)

View File

@@ -1,5 +1,76 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test", "objc_library")
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
WIN_SRCS = glob([
"src/main/native/windows/**/*.cpp",
"src/main/native/windows/**/*.h",
])
LINUX_SRCS = glob([
"src/main/native/linux/**/*.cpp",
"src/main/native/linux/**/*.h",
])
MAC_SRCS = glob(["src/main/native/osx/**/*.cpp"])
filegroup(
name = "native-srcs",
srcs = select({
"@bazel_tools//src/conditions:darwin": MAC_SRCS,
"@bazel_tools//src/conditions:windows": WIN_SRCS,
"@rules_bzlmodrio_toolchains//constraints/combined:is_linux": LINUX_SRCS,
}),
)
objc_library(
name = "cscore-mac",
srcs = glob([
"src/main/native/objcpp/**/*.mm",
"src/main/native/cpp/*.h",
]),
hdrs = glob([
"src/main/native/include/**/*",
"src/main/native/objcpp/**/*.h",
]),
copts = [
"-std=c++20",
],
includes = [
"src/main/native/cpp",
"src/main/native/include",
"src/main/native/objcpp",
],
tags = ["manual"],
deps = [
"//wpinet:wpinet.static",
"//wpiutil:wpiutil.static",
"@bzlmodrio-opencv//libraries/cpp/opencv",
],
)
cc_library(
name = "cscore.static",
srcs = [":native-srcs"] + glob(
["src/main/native/cpp/**"],
exclude = ["src/main/native/cpp/jni/**"],
),
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 = [
"//wpinet:wpinet.static",
"//wpiutil:wpiutil.static",
"@bzlmodrio-opencv//libraries/cpp/opencv",
] + select({
"@bazel_tools//src/conditions:darwin": [":cscore-mac"],
"//conditions:default": [],
}),
)
java_library(
name = "cscore-java",
srcs = glob(["src/main/java/**/*.java"]),
@@ -10,6 +81,24 @@ java_library(
],
)
cc_test(
name = "cscore-cpp-test",
size = "small",
srcs = glob(["src/test/native/**"]),
deps = [
":cscore.static",
"//thirdparty/googletest:googletest.static",
],
)
cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
deps = [
":cscore.static",
],
)
java_binary(
name = "DevMain-Java",
srcs = ["src/dev/java/edu/wpi/first/cscore/DevMain.java"],

View File

@@ -5,4 +5,4 @@ include(CompileWarnings)
file(GLOB developerRobotCpp_src src/main/native/cpp/*.cpp)
add_executable(developerRobotCpp ${developerRobotCpp_src})
target_link_libraries(developerRobotCpp wpilibc)
target_link_libraries(developerRobotCpp wpilibc wpilibNewCommands apriltag)

View File

@@ -133,6 +133,19 @@ deploy {
}
}
// Prevent the eclipse compiler (used by the VS Code extension for intellisense and debugging)
// from generating bad class files from annotation processors like Epilogue
eclipse {
classpath {
containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
file.whenMerged { cp ->
def entries = cp.entries;
def src = new org.gradle.plugins.ide.eclipse.model.SourceFolder('build/generated/sources/annotationProcessor/java/main/', null)
entries.add(src)
}
}
}
tasks.register('deployJava') {
try {
dependsOn tasks.named('deploydeveloperRobotJavasystemcore')

View File

@@ -0,0 +1,22 @@
load("@rules_java//java:defs.bzl", "java_library", "java_plugin")
java_library(
name = "processor",
srcs = glob(["src/main/java/**/*.java"]),
visibility = ["//visibility:public"],
runtime_deps = [
"//wpilibNewCommands:wpilibNewCommands-java",
],
deps = [
"//epilogue-runtime:epilogue",
],
)
java_plugin(
name = "plugin",
processor_class = "edu.wpi.first.epilogue.processor.AnnotationProcessor",
visibility = ["//visibility:public"],
deps = [
":processor",
],
)

View File

@@ -43,6 +43,8 @@ public class LoggerGenerator {
LoggerGenerator::isBuiltInJavaMethod;
private final ProcessingEnvironment m_processingEnv;
private final List<ElementHandler> m_handlers;
@SuppressWarnings("BadAnnotationImplementation")
private final Logged m_defaultConfig =
new Logged() {
@Override

View File

@@ -0,0 +1,12 @@
load("@rules_java//java:defs.bzl", "java_library")
java_library(
name = "epilogue",
srcs = glob(["src/main/java/**/*.java"]),
visibility = ["//visibility:public"],
deps = [
"//ntcore:networktables-java",
"//wpiunits",
"//wpiutil:wpiutil-java",
],
)

31
fieldImages/BUILD.bazel Normal file
View File

@@ -0,0 +1,31 @@
load("@rules_cc//cc:defs.bzl", "cc_library")
load("@rules_java//java:defs.bzl", "java_library")
load("//shared/bazel/rules/gen:gen-resources.bzl", "generate_resources")
generate_resources(
name = "generate-resources",
namespace = "fields",
prefix = "FIELDS",
resource_files = glob(["src/main/native/resources/**"]),
visibility = ["//visibility:public"],
)
cc_library(
name = "fieldImages",
srcs = [":generate-resources"] + glob(["src/main/native/cpp/**"]),
hdrs = glob(["src/main/native/include/**/*"]),
strip_include_prefix = "src/main/native/include",
visibility = ["//visibility:public"],
)
java_library(
name = "fieldImages-java",
srcs = glob(["src/main/java/**/*.java"]),
resource_strip_prefix = "fieldImages/src/main/native/resources",
resources = glob(["src/main/native/resources/**"]),
visibility = ["//visibility:public"],
deps = [
"@maven//:com_fasterxml_jackson_core_jackson_annotations",
"@maven//:com_fasterxml_jackson_core_jackson_databind",
],
)

View File

@@ -124,3 +124,4 @@ kResourceType_Koors40 = 122
kResourceType_ThriftyNova = 123
kResourceType_PWFSEN36005 = 124
kResourceType_LaserShark = 125
kResourceType_RevServoHub = 126

View File

@@ -271,6 +271,8 @@ public final class FRCNetComm {
public static final int kResourceType_PWFSEN36005 = 124;
/** kResourceType_LaserShark = 125. */
public static final int kResourceType_LaserShark = 125;
/** kResourceType_RevServoHub = 126. */
public static final int kResourceType_RevServoHub = 126;
}
/**

View File

@@ -177,6 +177,7 @@ namespace HALUsageReporting {
kResourceType_ThriftyNova = 123,
kResourceType_PWFSEN36005 = 124,
kResourceType_LaserShark = 125,
kResourceType_RevServoHub = 126,
};
enum tInstances : int32_t {
kLanguage_LabVIEW = 1,

View File

@@ -146,6 +146,7 @@ typedef enum
kResourceType_ThriftyNova = 123,
kResourceType_PWFSEN36005 = 124,
kResourceType_LaserShark = 125,
kResourceType_RevServoHub = 126,
// kResourceType_MaximumID = 255,
} tResourceType;

View File

@@ -47,6 +47,8 @@ public final class CANAPITypes {
kMiscellaneous(10),
/** IO breakout. */
kIOBreakout(11),
/** Servo Controller. */
kServoController(12),
/** Firmware update. */
kFirmwareUpdate(31);

View File

@@ -44,6 +44,8 @@ HAL_ENUM(HAL_CANDeviceType) {
HAL_CAN_Dev_kMiscellaneous = 10,
/// IO breakout.
HAL_CAN_Dev_kIOBreakout = 11,
// Servo controller.
HAL_CAN_Dev_kServoController = 12,
/// Firmware update.
HAL_CAN_Dev_kFirmwareUpdate = 31
};

53
romiVendordep/BUILD.bazel Normal file
View File

@@ -0,0 +1,53 @@
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 = "romi-cpp.static",
srcs = glob([
"src/main/native/cpp/**",
]),
hdrs = glob(["src/main/native/include/**"]),
strip_include_prefix = "src/main/native/include",
visibility = ["//visibility:public"],
deps = [
"//wpilibc:wpilibc.static",
],
)
java_library(
name = "romi-java",
srcs = glob(["src/main/java/**/*.java"]),
visibility = ["//visibility:public"],
deps = [
"//hal:hal-java",
"//wpilibj",
],
)
cc_test(
name = "romi-test",
size = "small",
srcs = glob(["src/test/native/cpp/**"]),
deps = [
"//thirdparty/googletest:googletest.static",
],
)
cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
deps = [
":romi-cpp.static",
],
)
java_binary(
name = "DevMain-Java",
srcs = ["src/dev/java/edu/wpi/first/wpilibj/romi/DevMain.java"],
main_class = "edu.wpi.first.wpilibj.romi.DevMain",
deps = [
"//hal:hal-java",
"//ntcore:networktables-java",
"//wpiutil:wpiutil-java",
],
)

View File

@@ -42,3 +42,6 @@ build:macos --linkopt=-Wl,-rpath,'@loader_path'"
# Things not in nativetools
build:macos --copt=-Wno-shorten-64-to-32
build:macos --host_per_file_copt=external/zlib/.*\.c@-Wno-deprecated-non-prototype
build:macos --host_per_file_copt=external/com_google_protobuf/.*\.cc@-Wno-unused-function

View File

@@ -14,3 +14,5 @@ build:roborio --cxxopt=-Wno-error=deprecated-declarations
# Extra 11
build:roborio --cxxopt=-Wno-error=deprecated-enum-enum-conversion
build:roborio --host_per_file_copt=external/zlib/.*\.c@-Wno-deprecated-non-prototype

View File

@@ -67,8 +67,9 @@ def tagList = [
"SmartDashboard", "Shuffleboard", "Sendable", "DataLog",
/* --- Controls --- */
"Exponential Profile", "PID", "State-Space", "Path Following", "Trajectory", "SysId",
"Simulation", "Trapezoid Profile", "Profiled PID", "Odometry", "LQR", "Pose Estimator",
"Exponential Profile", "PID", "State-Space", "LTVUnicycleController", "Path Following",
"Trajectory", "SysId", "Simulation", "Trapezoid Profile", "Profiled PID", "Odometry",
"LQR", "Pose Estimator",
/* --- Hardware --- */
"Analog", "Ultrasonic", "Gyro", "Pneumatics", "I2C", "Duty Cycle", "PDP", "DMA", "Relay",

View File

@@ -0,0 +1,87 @@
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 = ["//wpilibNewCommands:__subpackages__"],
)
filegroup(
name = "generated_cc_source",
srcs = glob(["src/generated/main/native/cpp/**"]),
visibility = ["//wpilibNewCommands:__subpackages__"],
)
filegroup(
name = "generated_java",
srcs = glob(["src/generated/main/java/**/*.java"]),
visibility = ["//wpilibNewCommands:__subpackages__"],
)
cc_library(
name = "wpilibNewCommands.static",
srcs = glob(["src/main/native/cpp/**"]) + [":generated_cc_source"],
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",
"//wpilibc:wpilibc.static",
],
)
java_library(
name = "wpilibNewCommands-java",
srcs = glob(["src/main/java/**/*.java"]) + [":generated_java"],
visibility = ["//visibility:public"],
deps = [
"//cscore:cscore-java",
"//hal:hal-java",
"//ntcore:networktables-java",
"//wpilibj",
"//wpimath:wpimath-java",
"//wpinet:wpinet-java",
"//wpiunits",
"//wpiutil:wpiutil-java",
],
)
cc_test(
name = "wpilibNewCommands-cpp-test",
size = "small",
srcs = glob([
"src/test/native/**/*.cpp",
"src/test/native/**/*.h",
]),
tags = [
"no-tsan",
"no-ubsan",
],
deps = [
":wpilibNewCommands.static",
"//thirdparty/googletest:googletest.static",
],
)
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/wpilibj2/commands/DevMain.java"],
main_class = "edu.wpi.first.wpilibj2.commands.DevMain",
deps = [
"//hal:hal-java",
"//ntcore:networktables-java",
"//wpimath:wpimath-java",
"//wpiutil:wpiutil-java",
],
)

View File

@@ -34,8 +34,7 @@ public class ProxyCommand extends Command {
* @deprecated This constructor's similarity to {@link DeferredCommand} is confusing and opens
* potential footguns for users who do not fully understand the semantics and implications of
* proxying, but who simply want runtime construction. Users who do know what they are doing
* and need a supplier-constructed proxied command should instead proxy a DeferredCommand
* using the <code>asProxy</code> decorator.
* and need a supplier-constructed proxied command should instead defer a proxy command.
* @see DeferredCommand
*/
@Deprecated(since = "2025", forRemoval = true)

View File

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

View File

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

View File

@@ -41,12 +41,11 @@ class ProxyCommand : public CommandHelper<Command, ProxyCommand> {
* confusing and opens potential footguns for users who do not fully
* understand the semantics and implications of proxying, but who simply want
* runtime construction. Users who do know what they are doing and need a
* supplier-constructed proxied command should instead proxy a DeferredCommand
* using the <code>AsProxy</code> decorator.
* supplier-constructed proxied command should instead defer a proxy command.
* @see DeferredCommand
*/
WPI_IGNORE_DEPRECATED
[[deprecated("Proxy a DeferredCommand instead")]]
[[deprecated("Defer a proxy command instead.")]]
explicit ProxyCommand(wpi::unique_function<Command*()> supplier);
/**
@@ -62,11 +61,10 @@ class ProxyCommand : public CommandHelper<Command, ProxyCommand> {
* confusing and opens potential footguns for users who do not fully
* understand the semantics and implications of proxying, but who simply want
* runtime construction. Users who do know what they are doing and need a
* supplier-constructed proxied command should instead proxy a DeferredCommand
* using the <code>AsProxy</code> decorator.
* supplier-constructed proxied command should instead defer a proxy command.
* @see DeferredCommand
*/
[[deprecated("Proxy a DeferredCommand instead")]]
[[deprecated("Defer a proxy command instead.")]]
explicit ProxyCommand(wpi::unique_function<CommandPtr()> supplier);
WPI_UNIGNORE_DEPRECATED

View File

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

80
wpilibc/BUILD.bazel Normal file
View File

@@ -0,0 +1,80 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load("//shared/bazel/rules/gen:gen-version-file.bzl", "generate_version_file")
generate_version_file(
name = "generate-version",
output_file = "WPILibVersion.cpp",
template = "src/generate/WPILibVersion.cpp.in",
visibility = ["//wpilibc:__subpackages__"],
)
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 = ["//wpilibc:__subpackages__"],
)
filegroup(
name = "generated_cc_source",
srcs = glob(
["src/generated/main/native/cpp/**"],
exclude = ["src/generated/main/native/cpp/jni/**"],
),
visibility = ["//wpilibc:__subpackages__"],
)
cc_library(
name = "wpilibc.static",
srcs = [
":generate-version",
] + glob([
"src/main/native/cppcs/**",
"src/main/native/cpp/**",
]) + [":generated_cc_source"],
hdrs = glob(["src/main/native/include/**"]),
strip_include_prefix = "src/main/native/include",
visibility = ["//visibility:public"],
deps = [
":generated_cc_headers",
"//cameraserver:cameraserver.static",
"//cscore:cscore.static",
"//hal:wpiHal.static",
"//ntcore:ntcore.static",
"//wpimath:wpimath.static",
"//wpinet:wpinet.static",
"//wpiutil:wpiutil.static",
],
)
cc_library(
name = "test-headers",
testonly = True,
hdrs = glob(["src/test/native/include/**"]),
includes = ["src/test/native/include"],
)
cc_test(
name = "wpilibc-test",
size = "small",
srcs = glob(["src/test/native/cpp/**"]),
tags = [
"no-asan",
"no-tsan",
"no-ubsan",
],
deps = [
":test-headers",
":wpilibc.static",
"//thirdparty/googletest:googletest.static",
],
)
cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
deps = [
":wpilibc.static",
],
)

View File

@@ -759,10 +759,11 @@
},
{
"name": "SimpleDifferentialDriveSimulation",
"description": "Simulate a differential drivetrain and follow trajectories with LTV unicycle controller (non-command-based).",
"description": "Simulate a differential drivetrain and follow trajectories with LTVUnicycleController (non-command-based).",
"tags": [
"Differential Drive",
"State-Space",
"LTVUnicycleController",
"Path Following",
"Trajectory",
"Encoder",

View File

@@ -0,0 +1,24 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
ATHENA_SOURCES = glob(["src/main/native/cpp/**"])
NON_ATHENA_SOURCES = glob(["src/main/native/dt/**"])
cc_library(
name = "test_headers",
hdrs = glob(["src/main/native/include/**"]),
strip_include_prefix = "src/main/native/include",
)
cc_binary(
name = "wpilibcIntegrationTests",
srcs = select({
"@rules_bzlmodrio_toolchains//constraints/is_roborio:roborio": ATHENA_SOURCES,
"//conditions:default": NON_ATHENA_SOURCES,
}),
deps = [
":test_headers",
"//thirdparty/googletest:googletest.static",
"//wpilibc:wpilibc.static",
],
)

View File

@@ -650,10 +650,11 @@
},
{
"name": "SimpleDifferentialDriveSimulation",
"description": "Simulate a differential drivetrain and follow trajectories with LTV unicycle controller (non-command-based).",
"description": "Simulate a differential drivetrain and follow trajectories with LTVUnicycleController (non-command-based).",
"tags": [
"Differential Drive",
"State-Space",
"LTVUnicycleController",
"Path Following",
"Trajectory",
"Encoder",

View File

@@ -1,3 +1,4 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
load("@rules_python//python:defs.bzl", "py_binary")
@@ -7,6 +8,76 @@ filegroup(
visibility = ["//wpimath:__subpackages__"],
)
cc_library(
name = "eigen-headers",
hdrs = glob([
"src/main/native/thirdparty/eigen/include/**",
]),
includes = ["src/main/native/thirdparty/eigen/include"],
strip_include_prefix = "src/main/native/thirdparty/eigen/include",
visibility = ["//wpimath:__subpackages__"],
)
cc_library(
name = "gcem",
hdrs = glob([
"src/main/native/thirdparty/gcem/include/**",
]),
includes = ["src/main/native/thirdparty/gcem/include"],
strip_include_prefix = "src/main/native/thirdparty/gcem/include",
visibility = ["//wpimath:__subpackages__"],
)
cc_library(
name = "sleipnir-headers",
hdrs = glob([
"src/main/native/thirdparty/sleipnir/include/**/*.hpp",
]),
includes = ["src/main/native/thirdparty/sleipnir/include"],
strip_include_prefix = "src/main/native/thirdparty/sleipnir/include",
visibility = ["//wpimath:__subpackages__"],
)
filegroup(
name = "sleipnir-srcs",
srcs = glob(["src/main/native/thirdparty/sleipnir/src/**"]),
visibility = ["//wpimath:__subpackages__"],
)
cc_library(
name = "nanopb-generated-headers",
hdrs = glob(["src/generated/main/native/cpp/**/*.h"]),
includes = ["src/generated/main/native/cpp"],
strip_include_prefix = "src/generated/main/native/cpp",
visibility = ["//wpiutil:__subpackages__"],
)
cc_library(
name = "wpimath.static",
srcs = glob(
[
"src/main/native/cpp/**",
"src/generated/main/native/cpp/**/*.cpp",
],
exclude = ["src/main/native/cpp/jni/**"],
) + [":sleipnir-srcs"],
hdrs = glob(["src/main/native/include/**"]),
defines = ["WPILIB_EXPORTS"],
includes = [
"src/main/native/include",
"src/main/native/thirdparty/sleipnir/src",
],
strip_include_prefix = "src/main/native/include",
visibility = ["//visibility:public"],
deps = [
":eigen-headers",
":gcem",
":nanopb-generated-headers",
":sleipnir-headers",
"//wpiutil:wpiutil.static",
],
)
java_library(
name = "wpimath-java",
srcs = [":generated_java"] + glob(["src/main/java/**/*.java"]),
@@ -24,6 +95,40 @@ java_library(
],
)
cc_library(
name = "test_headers",
hdrs = glob([
"src/test/native/include/**",
]),
strip_include_prefix = "src/test/native/include",
)
cc_test(
name = "wpimath-cpp-test",
size = "small",
srcs = glob([
"src/test/native/cpp/**/*.cpp",
"src/test/native/cpp/**/*.h",
]),
tags = [
"no-bullseye",
"no-raspi",
],
deps = [
":test_headers",
":wpimath.static",
"//thirdparty/googletest:googletest.static",
],
)
cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
deps = [
":wpimath.static",
],
)
java_binary(
name = "DevMain-Java",
srcs = ["src/dev/java/edu/wpi/first/math/DevMain.java"],

View File

@@ -80,6 +80,22 @@ public class WPINetJNI {
*/
public static native void removePortForwarder(int port);
/**
* Create a web server at the given port. Note that local ports less than 1024 won't work as a
* normal user.
*
* @param port local port number
* @param path local path to document root
*/
public static native void startWebServer(int port, String path);
/**
* Stop web server running at the given port.
*
* @param port local port number
*/
public static native void stopWebServer(int port);
/**
* Creates a MulticastServiceAnnouncer.
*

View File

@@ -0,0 +1,33 @@
// 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.net;
/** A web server using the HTTP protocol. */
public final class WebServer {
private WebServer() {
throw new UnsupportedOperationException("This is a utility class!");
}
/**
* Create a web server at the given port. Note that local ports less than 1024 won't work as a
* normal user. Also, many ports are blocked by the FRC robot radio; check the game manual for
* what is allowed through the radio firewall.
*
* @param port local port number
* @param path local path to document root
*/
public static void start(int port, String path) {
WPINetJNI.startWebServer(port, path);
}
/**
* Stop web server running at the given port.
*
* @param port local port number
*/
public static void stop(int port) {
WPINetJNI.stopWebServer(port);
}
}

View File

@@ -81,6 +81,22 @@ std::string_view EscapeURI(std::string_view str, SmallVectorImpl<char>& buf,
return {buf.data(), buf.size()};
}
std::string_view EscapeHTML(std::string_view str, SmallVectorImpl<char>& buf) {
buf.clear();
for (auto i = str.begin(), end = str.end(); i != end; ++i) {
if (*i == '&') {
buf.append({'&', 'a', 'm', 'p', ';'});
} else if (*i == '<') {
buf.append({'&', 'l', 't', ';'});
} else if (*i == '>') {
buf.append({'&', 'g', 't', ';'});
} else {
buf.push_back(*i);
}
}
return {buf.data(), buf.size()};
}
HttpQueryMap::HttpQueryMap(std::string_view query) {
SmallVector<std::string_view, 16> queryElems;
split(query, queryElems, '&', 100, false);

View File

@@ -0,0 +1,396 @@
// 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 "wpinet/WebServer.h"
#ifndef _WIN32
#include <unistd.h>
#endif
#include <memory>
#include <string>
#include <utility>
#include <fmt/format.h>
#include <wpi/DenseMap.h>
#include <wpi/MemoryBuffer.h>
#include <wpi/SmallString.h>
#include <wpi/Signal.h>
#include <wpi/StringMap.h>
#include <wpi/fs.h>
#include <wpi/json.h>
#include <wpi/print.h>
#include <wpi/raw_ostream.h>
#include "wpinet/EventLoopRunner.h"
#include "wpinet/HttpServerConnection.h"
#include "wpinet/HttpUtil.h"
#include "wpinet/UrlParser.h"
#include "wpinet/raw_uv_ostream.h"
#include "wpinet/uv/GetAddrInfo.h"
#include "wpinet/uv/Stream.h"
#include "wpinet/uv/Tcp.h"
#include "wpinet/uv/Timer.h"
using namespace wpi;
namespace {
class MyHttpConnection : public wpi::HttpServerConnection,
public std::enable_shared_from_this<MyHttpConnection> {
public:
explicit MyHttpConnection(std::shared_ptr<wpi::uv::Stream> stream,
std::string_view path)
: HttpServerConnection{std::move(stream)}, m_path{path} {}
protected:
void ProcessRequest() override;
void SendFileResponse(int code, std::string_view codeText,
std::string_view contentType, fs::path filename,
std::string_view extraHeader = {});
std::string m_path;
};
} // namespace
#ifndef _WIN32
namespace {
class SendfileReq : public uv::RequestImpl<SendfileReq, uv_fs_t> {
public:
SendfileReq(uv_file out, uv_file in, int64_t inOffset, size_t len)
: m_out(out), m_in(in), m_inOffset(inOffset), m_len(len) {
error = [this](uv::Error err) { GetLoop().error(err); };
}
uv::Loop& GetLoop() const {
return *static_cast<uv::Loop*>(GetRaw()->loop->data);
}
int Send(uv::Loop& loop) {
int err = uv_fs_sendfile(loop.GetRaw(), GetRaw(), m_out, m_in, m_inOffset,
m_len, [](uv_fs_t* req) {
auto& h = *static_cast<SendfileReq*>(req->data);
if (req->result < 0) {
h.ReportError(req->result);
h.complete();
h.Release();
return;
}
h.m_inOffset += req->result;
h.m_len -= req->result;
if (h.m_len == 0) {
// done
h.complete();
h.Release(); // this is always a one-shot
return;
}
// need to send more
h.Send(h.GetLoop());
});
if (err < 0) {
ReportError(err);
complete();
}
return err;
}
wpi::sig::Signal<> complete;
private:
uv_file m_out;
uv_file m_in;
int64_t m_inOffset;
size_t m_len;
};
} // namespace
static void Sendfile(uv::Loop& loop, uv_file out, uv_file in, int64_t inOffset,
size_t len, std::function<void()> complete) {
auto req = std::make_shared<SendfileReq>(out, in, inOffset, len);
if (complete) {
req->complete.connect(complete);
}
int err = req->Send(loop);
if (err >= 0) {
req->Keep();
}
}
#endif
static std::string_view GetMimeType(std::string_view ext) {
static const wpi::StringMap<std::string> map{
{"css", "text/css"},
{"csv", "text/csv"},
{"gif", "image/gif"},
{"htm", "text/html"},
{"html", "text/html"},
{"ico", "image/vnd.microsoft.icon"},
{"jar", "application/java-archive"},
{"jpeg", "image/jpeg"},
{"jpg", "image/jpeg"},
{"js", "text/javascript"},
{"json", "text/json"},
{"mjs", "text/javascript"},
{"pdf", "application/pdf"},
{"png", "image/png"},
{"sh", "application/x-sh"},
{"svg", "image/svg+xml"},
{"txt", "text/plain"},
{"webp", "image/webp"},
{"xhtml", "application/xhtml+xml"},
{"xml", "application/xml"},
{"zip", "application/zip"},
};
auto it = map.find(ext);
if (it == map.end()) {
return "application/octet-stream";
}
return it->second;
}
void MyHttpConnection::SendFileResponse(int code, std::string_view codeText,
std::string_view contentType,
fs::path filename,
std::string_view extraHeader) {
#ifdef _WIN32
auto membuf = wpi::MemoryBuffer::GetFile(filename.string());
if (!membuf) {
SendError(404);
return;
}
wpi::SmallVector<uv::Buffer, 4> toSend;
wpi::raw_uv_ostream os{toSend, 4096};
BuildHeader(os, code, codeText, contentType, (*membuf)->size(), extraHeader);
SendData(os.bufs(), false);
auto buf = (*membuf)->GetBuffer();
m_stream.Write(
{{buf}}, [closeAfter = !m_keepAlive, stream = &m_stream,
membuf = std::shared_ptr{std::move(*membuf)}](auto, uv::Error) {
if (closeAfter) {
stream->Close();
}
});
#else
// open file
std::error_code ec;
auto infile = fs::OpenFileForRead(filename, ec);
if (ec) {
SendError(404);
return;
}
int infd = fs::FileToFd(infile, ec, fs::OF_None);
if (ec) {
fs::CloseFile(infile);
SendError(404);
return;
}
// get file size
auto size = fs::file_size(filename, ec);
if (ec) {
SendError(404);
::close(infd);
return;
}
uv_os_fd_t outfd;
int err = uv_fileno(m_stream.GetRawHandle(), &outfd);
if (err < 0) {
m_stream.GetLoopRef().ReportError(err);
SendError(404);
::close(infd);
return;
}
wpi::SmallVector<uv::Buffer, 4> toSend;
wpi::raw_uv_ostream os{toSend, 4096};
BuildHeader(os, code, codeText, contentType, size, extraHeader);
SendData(os.bufs(), false);
// close after write completes if we aren't keeping alive
// since we're using sendfile, set socket to blocking
m_stream.SetBlocking(true);
Sendfile(m_stream.GetLoopRef(), outfd, infd, 0, size,
[infd, closeAfter = !m_keepAlive, stream = &m_stream] {
::close(infd);
if (closeAfter) {
stream->Close();
} else {
stream->SetBlocking(false);
}
});
#endif
}
void MyHttpConnection::ProcessRequest() {
// fmt::print(stderr, "HTTP request: '{}'\n", m_request.GetUrl());
wpi::UrlParser url{m_request.GetUrl(),
m_request.GetMethod() == wpi::HTTP_CONNECT};
if (!url.IsValid()) {
// failed to parse URL
SendError(400);
return;
}
std::string_view path;
if (url.HasPath()) {
path = url.GetPath();
}
// fmt::print(stderr, "path: \"{}\"\n", path);
wpi::SmallString<128> pathBuf;
bool error;
path = UnescapeURI(path, pathBuf, &error);
if (error) {
SendError(400);
return;
}
std::string_view query;
if (url.HasQuery()) {
query = url.GetQuery();
}
// fmt::print(stderr, "query: \"{}\"\n", query);
HttpQueryMap qmap{query};
const bool isGET = m_request.GetMethod() == wpi::HTTP_GET;
if (isGET && wpi::starts_with(path, '/') && !wpi::contains(path, "..")) {
fs::path fullpath = fmt::format("{}{}", m_path, path);
std::error_code ec;
bool isdir = fs::is_directory(fullpath, ec);
if (isdir) {
if (!wpi::ends_with(path, '/')) {
// redirect to trailing / location
SendResponse(301, "Moved Permanently", "text/plain", "",
fmt::format("Location: {}/\r\n\r\n", path));
return;
}
// generate directory listing
wpi::SmallString<64> formatBuf;
if (qmap.Get("format", formatBuf).value_or("") == "json") {
wpi::json dirs = wpi::json::array();
wpi::json files = wpi::json::array();
for (auto&& entry : fs::directory_iterator{fullpath}) {
bool subdir = entry.is_directory(ec);
std::string name = entry.path().filename().string();
if (subdir) {
dirs.emplace_back(wpi::json{{"name", std::move(name)}});
} else {
files.emplace_back(
wpi::json{{"name", std::move(name)},
{"size", subdir ? 0 : entry.file_size(ec)}});
}
}
SendResponse(
200, "OK", "text/json",
wpi::json{{"dirs", std::move(dirs)}, {"files", std::move(files)}}
.dump());
} else {
wpi::StringMap<std::string> dirs;
wpi::StringMap<std::string> files;
for (auto&& entry : fs::directory_iterator{fullpath}) {
bool subdir = entry.is_directory(ec);
std::string name = entry.path().filename().string();
wpi::SmallString<128> nameUriBuf, nameHtmlBuf;
if (subdir) {
dirs.emplace(
name, fmt::format(
"<tr><td><a href=\"{}/\">{}/</a></td><td></td></tr>",
EscapeURI(name, nameUriBuf),
EscapeHTML(name, nameHtmlBuf)));
} else {
files.emplace(
name, fmt::format(
"<tr><td><a href=\"{}\">{}</a></td><td>{}</td></tr>",
EscapeURI(name, nameUriBuf),
EscapeHTML(name, nameHtmlBuf), entry.file_size(ec)));
}
}
std::string html = fmt::format(
"<html><head><title>{}</title></head><body>"
"<table><tr><th>Name</th><th>Size</th></tr>\n",
path);
for (auto&& str : dirs) {
html += str.second;
}
for (auto&& str : files) {
html += str.second;
}
html += "</table></body></html>";
SendResponse(200, "OK", "text/html", html);
}
} else {
wpi::SmallString<128> extraHeadersBuf;
wpi::raw_svector_ostream os{extraHeadersBuf};
os << "Content-Disposition: filename=\"";
os.write_escaped(fullpath.filename().string());
os << "\"\r\n";
SendFileResponse(200, "OK", GetMimeType(wpi::rsplit(path, '.').second),
fullpath, os.str());
}
} else {
SendError(404, "Resource not found");
}
}
struct WebServer::Impl {
public:
EventLoopRunner runner;
DenseMap<unsigned int, std::weak_ptr<uv::Tcp>> servers;
};
WebServer::WebServer() : m_impl{new Impl} {}
WebServer& WebServer::GetInstance() {
static WebServer instance;
return instance;
}
void WebServer::Start(unsigned int port, std::string_view path) {
m_impl->runner.ExecSync([&](uv::Loop& loop) {
auto server = uv::Tcp::Create(loop);
if (!server) {
wpi::print(stderr, "WebServer: Creating server failed\n");
return;
}
// bind to local port
server->Bind("", port);
// when we get a connection, accept it
server->connection.connect(
[serverPtr = server.get(), path = std::string{path}] {
auto client = serverPtr->Accept();
if (!client) {
wpi::print(stderr, "WebServer: Connecting to client failed\n");
return;
}
// close on error
client->error.connect([clientPtr = client.get()](uv::Error err) {
clientPtr->Close();
});
auto conn = std::make_shared<MyHttpConnection>(client, path);
client->SetData(conn);
});
// start listening for incoming connections
server->Listen();
m_impl->servers[port] = server;
});
}
void WebServer::Stop(unsigned int port) {
m_impl->runner.ExecSync([&](uv::Loop& loop) {
if (auto server = m_impl->servers.lookup(port).lock()) {
server->Close();
m_impl->servers.erase(port);
}
});
}

View File

@@ -16,6 +16,7 @@
#include "wpinet/MulticastServiceAnnouncer.h"
#include "wpinet/MulticastServiceResolver.h"
#include "wpinet/PortForwarder.h"
#include "wpinet/WebServer.h"
using namespace wpi::java;
@@ -80,6 +81,31 @@ Java_edu_wpi_first_net_WPINetJNI_removePortForwarder
wpi::PortForwarder::GetInstance().Remove(port);
}
/*
* Class: edu_wpi_first_net_WPINetJNI
* Method: startWebServer
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_net_WPINetJNI_startWebServer
(JNIEnv* env, jclass, jint port, jstring path)
{
wpi::WebServer::GetInstance().Start(static_cast<unsigned int>(port),
JStringRef{env, path}.str());
}
/*
* Class: edu_wpi_first_net_WPINetJNI
* Method: stopWebServer
* Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_net_WPINetJNI_stopWebServer
(JNIEnv* env, jclass, jint port)
{
wpi::WebServer::GetInstance().Stop(port);
}
/*
* Class: edu_wpi_first_net_WPINetJNI
* Method: createMulticastServiceAnnouncer

View File

@@ -39,6 +39,11 @@ std::string_view UnescapeURI(std::string_view str, SmallVectorImpl<char>& buf,
std::string_view EscapeURI(std::string_view str, SmallVectorImpl<char>& buf,
bool spacePlus = true);
// Escape a string for HTML output.
// @param buf Buffer for output
// @return Escaped string
std::string_view EscapeHTML(std::string_view str, SmallVectorImpl<char>& buf);
// Parse a set of HTTP headers. Saves just the Content-Type and Content-Length
// fields.
// @param is Input stream

View File

@@ -0,0 +1,58 @@
// 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 WPINET_WEBSERVER_H_
#define WPINET_WEBSERVER_H_
#pragma once
#include <memory>
#include <string_view>
namespace wpi {
/**
* A web server using the HTTP protocol.
*/
class WebServer {
public:
WebServer(const WebServer&) = delete;
WebServer& operator=(const WebServer&) = delete;
/**
* Get an instance of the WebServer class.
*
* This is a singleton to guarantee that there is only a single instance
* regardless of how many times GetInstance is called.
*/
static WebServer& GetInstance();
/**
* Create a web server at the given port.
* Note that local ports less than 1024 won't work as a normal user. Also,
* many ports are blocked by the FRC robot radio; check the game manual for
* what is allowed through the radio firewall.
*
* @param port local port number
* @param path local path to document root
*/
void Start(unsigned int port, std::string_view path);
/**
* Stop web server running at the given port.
*
* @param port local port number
*/
void Stop(unsigned int port);
private:
WebServer();
struct Impl;
std::unique_ptr<Impl> m_impl;
};
} // namespace wpi
#endif // WPINET_WEBSERVER_H_

View File

@@ -28,7 +28,7 @@ public final class MomentOfInertiaUnit extends PerUnit<AngularMomentumUnit, Angu
}
MomentOfInertiaUnit(
PerUnit<AngularMomentumUnit, AngularVelocityUnit> baseUnit,
MomentOfInertiaUnit baseUnit,
UnaryFunction toBaseConverter,
UnaryFunction fromBaseConverter,
String name,

View File

@@ -197,9 +197,9 @@ cc_library(
":llvm-srcs",
":memory-srcs",
":mpack-srcs",
":nanopb-srcs",
":native-srcs",
":protobuf-srcs",
":nanopb-srcs",
],
hdrs = glob(["src/main/native/include/**/*"]),
includes = ["src/main/native/include"],
@@ -215,8 +215,8 @@ cc_library(
":llvm-headers",
":memory-headers",
":mpack-headers",
":protobuf-headers",
":nanopb-headers",
":protobuf-headers",
":sigslot-headers",
] + select({
"@rules_bzlmodrio_toolchains//constraints/is_roborio:roborio": ["@bzlmodrio-ni//libraries/cpp/ni:shared"],

View File

@@ -16,17 +16,20 @@ public final class RuntimeLoader {
* @return A load error message.
*/
private static String getLoadErrorMessage(String libraryName, UnsatisfiedLinkError ule) {
String jvmLocation = ProcessHandle.current().info().command().orElse("Unknown");
StringBuilder msg = new StringBuilder(512);
msg.append(libraryName)
.append(" could not be loaded from path.\n" + "\tattempted to load for platform ")
.append(CombinedRuntimeLoader.getPlatformPath())
.append("\nLast Load Error: \n")
.append(ule.getMessage())
.append('\n');
.append('\n')
.append(String.format("JVM Location: %s\n", jvmLocation));
if (System.getProperty("os.name").startsWith("Windows")) {
msg.append(
"A common cause of this error is missing the C++ runtime.\n"
+ "Download the latest at https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads\n");
"A common cause of this error is using a JVM with an incorrect MSVC runtime.\n"
+ "Ensure you are using the WPILib JVM (The current running JVM is listed above)\n"
+ "See https://wpilib.org/jvmruntime for more information\n");
}
return msg.toString();
}

54
xrpVendordep/BUILD.bazel Normal file
View File

@@ -0,0 +1,54 @@
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 = "xrp-cpp",
srcs = glob([
"src/main/native/cpp/**",
]),
hdrs = glob(["src/main/native/include/**"]),
strip_include_prefix = "src/main/native/include",
visibility = ["//visibility:public"],
deps = [
"//wpilibc:wpilibc.static",
],
)
java_library(
name = "xrp-java",
srcs = glob(["src/main/java/**/*.java"]),
visibility = ["//visibility:public"],
deps = [
"//hal:hal-java",
"//wpilibj",
"//wpimath:wpimath-java",
],
)
cc_test(
name = "xrp-cpp-test",
size = "small",
srcs = glob(["src/test/native/cpp/**"]),
deps = [
"//thirdparty/googletest:googletest.static",
],
)
cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
deps = [
"//wpiutil:wpiutil.static",
],
)
java_binary(
name = "DevMain-Java",
srcs = ["src/dev/java/edu/wpi/first/wpilibj/xrp/DevMain.java"],
main_class = "edu.wpi.first.wpilibj.xrp.DevMain",
deps = [
"//hal:hal-java",
"//ntcore:networktables-java",
"//wpiutil:wpiutil-java",
],
)