mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-29 02:21:44 +00:00
Compare commits
89 Commits
v2024.1.1-
...
v2024.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4809f3d0fc | ||
|
|
dd90965362 | ||
|
|
8659372d08 | ||
|
|
a2e4d0b15d | ||
|
|
0a46a3a618 | ||
|
|
7c26bc70ab | ||
|
|
f94e3d81b9 | ||
|
|
6bed82a18e | ||
|
|
4595f84719 | ||
|
|
707cb06105 | ||
|
|
3e40b9e5da | ||
|
|
106518c3f8 | ||
|
|
19cb2a8eb4 | ||
|
|
13f4460e00 | ||
|
|
4210f5635d | ||
|
|
0f060afb55 | ||
|
|
f29a7d2e50 | ||
|
|
6e58db398d | ||
|
|
4ac0720385 | ||
|
|
44db3e0ac0 | ||
|
|
73c7d87db7 | ||
|
|
25636b712f | ||
|
|
01fb98baaa | ||
|
|
5c424248c4 | ||
|
|
c486972c55 | ||
|
|
783acb9b72 | ||
|
|
99ab836894 | ||
|
|
ad0859a8c9 | ||
|
|
5579219716 | ||
|
|
98f06911c7 | ||
|
|
e1d49b975c | ||
|
|
8a0bf2b7a4 | ||
|
|
91d8837c11 | ||
|
|
e7c9f27683 | ||
|
|
8aca706217 | ||
|
|
7d3e4ddba9 | ||
|
|
ec3cb3dcba | ||
|
|
495585b25d | ||
|
|
f9aabc5ab2 | ||
|
|
c16946c0ec | ||
|
|
b7f4eb2811 | ||
|
|
f419a62b38 | ||
|
|
938bf45fd9 | ||
|
|
c34debe012 | ||
|
|
07183765de | ||
|
|
af46034b7f | ||
|
|
636ef58d94 | ||
|
|
cc631d2a69 | ||
|
|
09f76b32c2 | ||
|
|
47c5fd8620 | ||
|
|
24a76be694 | ||
|
|
9333951736 | ||
|
|
6a2d3c30a6 | ||
|
|
e07de37e64 | ||
|
|
141241d2d6 | ||
|
|
f2c2bab7dc | ||
|
|
5659038443 | ||
|
|
8aeee03626 | ||
|
|
55508706ff | ||
|
|
ab78b930e9 | ||
|
|
795d4be9fd | ||
|
|
7aa9ad44b8 | ||
|
|
92c81d0791 | ||
|
|
1ce617be07 | ||
|
|
2441b57156 | ||
|
|
21d1972d7a | ||
|
|
c29e8c66cf | ||
|
|
ab309e34ef | ||
|
|
22a322c9f3 | ||
|
|
1dba26c937 | ||
|
|
ef1cb3f41e | ||
|
|
aeb1a4aa33 | ||
|
|
c1178d5add | ||
|
|
4e4a468d4d | ||
|
|
d1793f077d | ||
|
|
43fb6e9f87 | ||
|
|
bcef6c5398 | ||
|
|
4059e0cd9f | ||
|
|
0b2cfb3abc | ||
|
|
df5e439b0c | ||
|
|
0ff7478968 | ||
|
|
6f23d32fe1 | ||
|
|
35a1c52788 | ||
|
|
e4e2bafdb1 | ||
|
|
3d201c71f7 | ||
|
|
f02984159f | ||
|
|
a004c9e05f | ||
|
|
0b4c6a1546 | ||
|
|
ab15dae887 |
@@ -145,6 +145,8 @@ LambdaBodyIndentation: Signature
|
||||
LineEnding: DeriveLF
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
Macros:
|
||||
- 'HAL_ENUM(name)=enum name'
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Never
|
||||
|
||||
@@ -49,7 +49,6 @@ Checks:
|
||||
google-build-namespaces,
|
||||
google-explicit-constructor,
|
||||
google-global-names-in-headers,
|
||||
google-readability-avoid-underscore-in-googletest-name,
|
||||
google-readability-casting,
|
||||
google-runtime-operator,
|
||||
misc-definitions-in-headers,
|
||||
|
||||
2
.github/workflows/cmake.yml
vendored
2
.github/workflows/cmake.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
- name: Install QuickBuffers (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.2/protoc-gen-quickbuf_1.3.2_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.2_amd64.deb
|
||||
run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf_1.3.3_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.3_amd64.deb
|
||||
|
||||
- name: Install opencv (macOS)
|
||||
run: brew install opencv protobuf@3 ninja
|
||||
|
||||
10
.github/workflows/gradle.yml
vendored
10
.github/workflows/gradle.yml
vendored
@@ -54,7 +54,7 @@ jobs:
|
||||
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
- name: Check free disk space
|
||||
run: df .
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.artifact-name }}
|
||||
path: build/allOutputs
|
||||
@@ -159,7 +159,7 @@ jobs:
|
||||
- name: Check disk free space (Windows)
|
||||
run: wmic logicaldisk get caption, freespace
|
||||
if: matrix.os == 'windows-2022'
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.artifact-name }}
|
||||
path: ${{ matrix.build-dir }}/${{ matrix.outputs }}
|
||||
@@ -183,7 +183,7 @@ jobs:
|
||||
env:
|
||||
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Documentation
|
||||
path: docs/build/outputs
|
||||
@@ -212,7 +212,7 @@ jobs:
|
||||
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
|
||||
with:
|
||||
repository: wpilibsuite/build-tools
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
if: |
|
||||
github.repository_owner == 'wpilibsuite' &&
|
||||
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
|
||||
@@ -255,7 +255,7 @@ jobs:
|
||||
RUN_AZURE_ARTIFACTORY_RELEASE: "TRUE"
|
||||
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: |
|
||||
github.repository_owner == 'wpilibsuite' &&
|
||||
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
|
||||
|
||||
2
.github/workflows/lint-format.yml
vendored
2
.github/workflows/lint-format.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
- name: Generate diff
|
||||
run: git diff HEAD > wpiformat-fixes.patch
|
||||
if: ${{ failure() }}
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wpiformat fixes
|
||||
path: wpiformat-fixes.patch
|
||||
|
||||
15
.github/workflows/pregenerate.yml
vendored
15
.github/workflows/pregenerate.yml
vendored
@@ -15,12 +15,9 @@ jobs:
|
||||
name: "Update"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Fetch all history and metadata
|
||||
run: |
|
||||
git fetch --prune --unshallow
|
||||
git checkout -b pr
|
||||
git branch -f main origin/main
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
@@ -28,13 +25,13 @@ jobs:
|
||||
- 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.2/protoc-gen-quickbuf-1.3.2-linux-x86_64.exe && chmod +x protoc-gen-quickbuf-1.3.2-linux-x86_64.exe
|
||||
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: Run hal
|
||||
run: ./hal/generate_usage_reporting.py
|
||||
- name: Run ntcore
|
||||
run: ./ntcore/generate_topics.py
|
||||
- name: Run wpimath
|
||||
run: ./wpimath/generate_numbers.py && ./wpimath/generate_quickbuf.py protoc protoc-gen-quickbuf-1.3.2-linux-x86_64.exe
|
||||
run: ./wpimath/generate_numbers.py && ./wpimath/generate_quickbuf.py protoc protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
|
||||
- name: Add untracked files to index so they count as changes
|
||||
run: git add -A
|
||||
- name: Check output
|
||||
@@ -42,7 +39,7 @@ jobs:
|
||||
- name: Generate diff
|
||||
run: git diff HEAD > pregenerated-files-fixes.patch
|
||||
if: ${{ failure() }}
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pregenerated-files-fixes
|
||||
path: pregenerated-files-fixes.patch
|
||||
|
||||
2
.github/workflows/sanitizers.yml
vendored
2
.github/workflows/sanitizers.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
|
||||
- name: Install QuickBuffers
|
||||
if: runner.os == 'Linux'
|
||||
run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.2/protoc-gen-quickbuf_1.3.2_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.2_amd64.deb
|
||||
run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf_1.3.3_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.3_amd64.deb
|
||||
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.3
|
||||
|
||||
@@ -54,7 +54,7 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir)
|
||||
if("${isSystemDir}" STREQUAL "-1")
|
||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
|
||||
endif("${isSystemDir}" STREQUAL "-1")
|
||||
endif()
|
||||
|
||||
# Options for building certain parts of the repo. Everything is built by default.
|
||||
option(BUILD_SHARED_LIBS "Build with shared libs (needed for JNI)" ON)
|
||||
@@ -82,6 +82,9 @@ option(USE_SYSTEM_EIGEN "Use system eigen" OFF)
|
||||
# Options for location of OpenCV Java.
|
||||
set(OPENCV_JAVA_INSTALL_DIR "" CACHE PATH "Location to search for the OpenCV jar file")
|
||||
|
||||
# Options for compilation flags.
|
||||
option(NO_WERROR "Disable -Werror flag during compilation" OFF)
|
||||
|
||||
# Set default build type to release with debug info (i.e. release mode optimizations
|
||||
# are performed, but debug info still exists).
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
@@ -211,7 +214,6 @@ endif()
|
||||
find_package(LIBSSH 0.7.1)
|
||||
|
||||
find_package(Protobuf REQUIRED)
|
||||
find_program(Quickbuf_EXECUTABLE NAMES protoc-gen-quickbuf DOC "The Quickbuf protoc plugin")
|
||||
|
||||
set(APRILTAG_DEP_REPLACE "find_dependency(apriltag)")
|
||||
set(CAMERASERVER_DEP_REPLACE_IMPL "find_dependency(cameraserver)")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## Publishing Third Party Dependencies
|
||||
Currently the 3rd party deps are imgui, opencv, and google test
|
||||
Currently the 3rd party deps are imgui, opencv, google test, libssh, and apriltaglib
|
||||
|
||||
For publishing these dependencies, the version needs to be manually updated in the publish.gradle file of their respective repository.
|
||||
Then, in the azure build for the dependency you want to build for, manually start a pipeline build (As of current, this is the `Run Pipeline` button).
|
||||
@@ -24,4 +24,4 @@ Upon pushing a tag, a release will be built, and the files will be uploaded to t
|
||||
Before publishing, make sure to update the version in build.gradle. Publishing must happen locally, using the command `./gradlew publishPlugin`. This does require your API key for publishing to be set.
|
||||
|
||||
## Building the installer
|
||||
Update the GradleRIO version in gradle.properties, and in the scripts folder in vscode, update the vscode extension. Then push, it will build the installer on azure.
|
||||
Update the GradleRIO version in gradle.properties, and in the scripts folder in vscode, update the vscode extension. To publish a release build, upload a new tag, and a release will automatically be built and published to artifactory and cloudflare.
|
||||
|
||||
@@ -145,6 +145,11 @@ All artifacts are based at `edu.wpi.first.artifactname` in the repository.
|
||||
* wpinet
|
||||
* wpiutil
|
||||
|
||||
* wpiunits
|
||||
|
||||
* apriltag
|
||||
* wpiutil
|
||||
* wpimath
|
||||
|
||||
### Third Party Artifacts
|
||||
|
||||
@@ -152,6 +157,7 @@ This repository provides the builds of the following third party software.
|
||||
|
||||
All artifacts are based at `edu.wpi.first.thirdparty.frcYEAR` in the repository.
|
||||
|
||||
* apriltaglib
|
||||
* googletest
|
||||
* imgui
|
||||
* opencv
|
||||
|
||||
@@ -63,6 +63,8 @@ The following build options are available:
|
||||
* TODO
|
||||
* `OPENCV_JAVA_INSTALL_DIR`
|
||||
* Set this option to the location of the archive of the OpenCV Java bindings (it should be called opencv-xxx.jar, with the x'es being version numbers). NOTE: set it to the LOCATION of the file, not the file itself!
|
||||
* `NO_WERROR` (OFF Default)
|
||||
* This option will disable the `-Werror` compilation flag for non-MSVC builds.
|
||||
|
||||
## Build Setup
|
||||
|
||||
|
||||
28
apriltag/README.md
Normal file
28
apriltag/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# AprilTag
|
||||
|
||||
## Adding new field to AprilTagFields
|
||||
|
||||
### Adding field JSON
|
||||
|
||||
1. Add a field layout CSV file to `src/main/native/resources/edu/wpi/first/apriltag`
|
||||
1. See docstring in `convert_apriltag_layouts.py` for more
|
||||
2. Run `convert_apriltag_layouts.py` in the same directory as this readme to generate the JSON
|
||||
3. That script overwrites all generated JSONs, so undo undesired changes if necessary
|
||||
4. Update the field dimensions at the bottom of the JSON
|
||||
1. Length should be in meters from alliance wall to alliance wall
|
||||
2. Width should be in meters from inside guardrail plastic to plastic
|
||||
|
||||
### Java updates
|
||||
|
||||
1. Update `src/main/java/edu/wpi/first/apriltag/AprilTagFields.java`
|
||||
1. Add enum value for new field to `AprilTagFields`
|
||||
2. Update `AprilTagFields.kDefaultField` if necessary
|
||||
|
||||
### C++ updates
|
||||
|
||||
1. Update `src/main/native/include/frc/apriltag/AprilTagFields.h`
|
||||
1. Add enum value for new field to `AprilTagFields`
|
||||
2. Update `AprilTagFields::kDefaultField` if necessary
|
||||
2. Update `src/main/native/cpp/AprilTagFields.cpp`
|
||||
1. Add resource getter prototype like `std::string_view GetResource_2024_crescendo_json()`
|
||||
2. Add case for new field to switch in `LoadAprilTagLayoutField()`
|
||||
88
apriltag/convert_apriltag_layouts.py
Executable file
88
apriltag/convert_apriltag_layouts.py
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
This script converts all AprilTag field layout CSV files in
|
||||
src/main/native/resources/edu/wpi/first/apriltag to the JSON format
|
||||
AprilTagFields expects.
|
||||
|
||||
The input CSV has the following format:
|
||||
|
||||
* Columns: ID, X, Y, Z, Rotation
|
||||
* ID is a positive integer
|
||||
* X, Y, and Z are decimal inches
|
||||
* Rotation is yaw in degrees
|
||||
|
||||
The values come from a table in the layout marking diagram (e.g.,
|
||||
https://firstfrc.blob.core.windows.net/frc2024/FieldAssets/2024LayoutMarkingDiagram.pdf).
|
||||
"""
|
||||
|
||||
import csv
|
||||
import json
|
||||
import os
|
||||
|
||||
from wpimath import geometry, units
|
||||
import numpy as np
|
||||
|
||||
|
||||
def main():
|
||||
# Find AprilTag field layout CSVs
|
||||
filenames = [
|
||||
os.path.join(dp, f)
|
||||
for dp, dn, fn in os.walk("src/main/native/resources/edu/wpi/first/apriltag")
|
||||
for f in fn
|
||||
if f.endswith(".csv")
|
||||
]
|
||||
|
||||
for filename in filenames:
|
||||
json_data = {"tags": [], "field": {"length": 0.0, "width": 0.0}}
|
||||
|
||||
# Read CSV and fill in JSON data
|
||||
with open(filename, newline="") as csvfile:
|
||||
reader = csv.reader(csvfile, delimiter=",")
|
||||
|
||||
# Skip header
|
||||
next(reader)
|
||||
|
||||
for row in reader:
|
||||
# Unpack row elements
|
||||
id = int(row[0])
|
||||
x = float(row[1])
|
||||
y = float(row[2])
|
||||
z = float(row[3])
|
||||
rotation = float(row[4])
|
||||
|
||||
# Turn yaw into quaternion
|
||||
q = geometry.Rotation3d(
|
||||
units.radians(0.0),
|
||||
units.radians(0.0),
|
||||
units.degreesToRadians(rotation),
|
||||
).getQuaternion()
|
||||
|
||||
json_data["tags"].append(
|
||||
{
|
||||
"ID": id,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": units.inchesToMeters(x),
|
||||
"y": units.inchesToMeters(y),
|
||||
"z": units.inchesToMeters(z),
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": q.W(),
|
||||
"X": q.X(),
|
||||
"Y": q.Y(),
|
||||
"Z": q.Z(),
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
# Write JSON
|
||||
with open(filename.replace(".csv", ".json"), "w") as f:
|
||||
json.dump(json_data, f, indent=2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -11,14 +11,23 @@ import edu.wpi.first.math.geometry.Pose3d;
|
||||
import edu.wpi.first.util.RawFrame;
|
||||
import java.util.Objects;
|
||||
|
||||
/** Represents an AprilTag's metadata. */
|
||||
@SuppressWarnings("MemberName")
|
||||
public class AprilTag {
|
||||
/** The tag's ID. */
|
||||
@JsonProperty(value = "ID")
|
||||
public int ID;
|
||||
|
||||
/** The tag's pose. */
|
||||
@JsonProperty(value = "pose")
|
||||
public Pose3d pose;
|
||||
|
||||
/**
|
||||
* Constructs an AprilTag.
|
||||
*
|
||||
* @param ID The tag's ID.
|
||||
* @param pose The tag's pose.
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
@JsonCreator
|
||||
public AprilTag(
|
||||
@@ -54,9 +63,9 @@ public class AprilTag {
|
||||
* @return A RawFrame containing the AprilTag image
|
||||
*/
|
||||
public static RawFrame generate16h5AprilTagImage(int id) {
|
||||
RawFrame generatedImage = new RawFrame();
|
||||
AprilTagJNI.generate16h5AprilTagImage(id, generatedImage.getDataPtr());
|
||||
return generatedImage;
|
||||
RawFrame frame = new RawFrame();
|
||||
AprilTagJNI.generate16h5AprilTagImage(frame, frame.getNativeObj(), id);
|
||||
return frame;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,8 +75,8 @@ public class AprilTag {
|
||||
* @return A RawFrame containing the AprilTag image
|
||||
*/
|
||||
public static RawFrame generate36h11AprilTagImage(int id) {
|
||||
RawFrame generatedImage = new RawFrame();
|
||||
AprilTagJNI.generate36h11AprilTagImage(id, generatedImage.getDataPtr());
|
||||
return generatedImage;
|
||||
RawFrame frame = new RawFrame();
|
||||
AprilTagJNI.generate36h11AprilTagImage(frame, frame.getNativeObj(), id);
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +57,21 @@ public class AprilTagDetector implements AutoCloseable {
|
||||
*/
|
||||
public boolean debug;
|
||||
|
||||
/** Default constructor. */
|
||||
public Config() {}
|
||||
|
||||
/**
|
||||
* Constructs a detector configuration.
|
||||
*
|
||||
* @param numThreads How many threads should be used for computation.
|
||||
* @param quadDecimate Quad decimation.
|
||||
* @param quadSigma What Gaussian blur should be applied to the segmented image (used for quad
|
||||
* detection).
|
||||
* @param refineEdges When true, the edges of the each quad are adjusted to "snap to" strong
|
||||
* gradients nearby.
|
||||
* @param decodeSharpening How much sharpening should be done to decoded images.
|
||||
* @param debug Debug mode.
|
||||
*/
|
||||
Config(
|
||||
int numThreads,
|
||||
float quadDecimate,
|
||||
@@ -139,8 +152,21 @@ public class AprilTagDetector implements AutoCloseable {
|
||||
*/
|
||||
public boolean deglitch;
|
||||
|
||||
/** Default constructor. */
|
||||
public QuadThresholdParameters() {}
|
||||
|
||||
/**
|
||||
* Constructs quad threshold parameters.
|
||||
*
|
||||
* @param minClusterPixels Threshold used to reject quads containing too few pixels.
|
||||
* @param maxNumMaxima How many corner candidates to consider when segmenting a group of pixels
|
||||
* into a quad.
|
||||
* @param criticalAngle Critical angle, in radians.
|
||||
* @param maxLineFitMSE When fitting lines to the contours, the maximum mean squared error
|
||||
* allowed.
|
||||
* @param minWhiteBlackDiff Minimum brightness offset.
|
||||
* @param deglitch Whether the thresholded image be should be deglitched.
|
||||
*/
|
||||
QuadThresholdParameters(
|
||||
int minClusterPixels,
|
||||
int maxNumMaxima,
|
||||
@@ -182,6 +208,7 @@ public class AprilTagDetector implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
/** Constructs an AprilTagDetector. */
|
||||
public AprilTagDetector() {
|
||||
m_native = AprilTagJNI.createDetector();
|
||||
}
|
||||
|
||||
@@ -44,8 +44,11 @@ import java.util.Optional;
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
public class AprilTagFieldLayout {
|
||||
/** Common origin positions for the AprilTag coordinate system. */
|
||||
public enum OriginPosition {
|
||||
/** Blue alliance wall, right side. */
|
||||
kBlueAllianceWallRightSide,
|
||||
/** Red alliance wall, right side. */
|
||||
kRedAllianceWallRightSide,
|
||||
}
|
||||
|
||||
|
||||
@@ -7,15 +7,22 @@ package edu.wpi.first.apriltag;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
|
||||
/** Loadable AprilTag field layouts. */
|
||||
public enum AprilTagFields {
|
||||
/** 2022 Rapid React. */
|
||||
k2022RapidReact("2022-rapidreact.json"),
|
||||
k2023ChargedUp("2023-chargedup.json");
|
||||
/** 2023 Charged Up. */
|
||||
k2023ChargedUp("2023-chargedup.json"),
|
||||
/** 2024 Crescendo. */
|
||||
k2024Crescendo("2024-crescendo.json");
|
||||
|
||||
/** Base resource directory. */
|
||||
public static final String kBaseResourceDir = "/edu/wpi/first/apriltag/";
|
||||
|
||||
/** Alias to the current game. */
|
||||
public static final AprilTagFields kDefaultField = k2023ChargedUp;
|
||||
public static final AprilTagFields kDefaultField = k2024Crescendo;
|
||||
|
||||
/** Resource filename. */
|
||||
public final String m_resourceFile;
|
||||
|
||||
AprilTagFields(String resourceFile) {
|
||||
|
||||
@@ -29,10 +29,19 @@ public class AprilTagPoseEstimator {
|
||||
this.cy = cy;
|
||||
}
|
||||
|
||||
/** Tag size, in meters. */
|
||||
public double tagSize;
|
||||
|
||||
/** Camera horizontal focal length, in pixels. */
|
||||
public double fx;
|
||||
|
||||
/** Camera vertical focal length, in pixels. */
|
||||
public double fy;
|
||||
|
||||
/** Camera horizontal focal center, in pixels. */
|
||||
public double cx;
|
||||
|
||||
/** Camera vertical focal center, in pixels. */
|
||||
public double cy;
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,25 +8,41 @@ import edu.wpi.first.apriltag.AprilTagDetection;
|
||||
import edu.wpi.first.apriltag.AprilTagDetector;
|
||||
import edu.wpi.first.apriltag.AprilTagPoseEstimate;
|
||||
import edu.wpi.first.math.geometry.Transform3d;
|
||||
import edu.wpi.first.util.RawFrame;
|
||||
import edu.wpi.first.util.RuntimeLoader;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/** AprilTag JNI. */
|
||||
public class AprilTagJNI {
|
||||
static boolean libraryLoaded = false;
|
||||
|
||||
static RuntimeLoader<AprilTagJNI> loader = null;
|
||||
|
||||
/** Sets whether JNI should be loaded in the static block. */
|
||||
public static class Helper {
|
||||
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
|
||||
|
||||
/**
|
||||
* Returns true if the JNI should be loaded in the static block.
|
||||
*
|
||||
* @return True if the JNI should be loaded in the static block.
|
||||
*/
|
||||
public static boolean getExtractOnStaticLoad() {
|
||||
return extractOnStaticLoad.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the JNI should be loaded in the static block.
|
||||
*
|
||||
* @param load Whether the JNI should be loaded in the static block.
|
||||
*/
|
||||
public static void setExtractOnStaticLoad(boolean load) {
|
||||
extractOnStaticLoad.set(load);
|
||||
}
|
||||
|
||||
/** Utility class. */
|
||||
private Helper() {}
|
||||
}
|
||||
|
||||
static {
|
||||
@@ -190,7 +206,24 @@ public class AprilTagJNI {
|
||||
double cx,
|
||||
double cy);
|
||||
|
||||
public static native void generate16h5AprilTagImage(int id, long nativeAddr);
|
||||
/**
|
||||
* Generates a RawFrame containing the apriltag with the id with family 16h5 passed in.
|
||||
*
|
||||
* @param frameObj generated frame (output parameter).
|
||||
* @param frame raw frame handle
|
||||
* @param id id
|
||||
*/
|
||||
public static native void generate16h5AprilTagImage(RawFrame frameObj, long frame, int id);
|
||||
|
||||
public static native void generate36h11AprilTagImage(int id, long nativeAddr);
|
||||
/**
|
||||
* Generates a RawFrame containing the apriltag with the id with family 36h11 passed in.
|
||||
*
|
||||
* @param frameObj generated frame (output parameter).
|
||||
* @param frame raw frame handle
|
||||
* @param id id
|
||||
*/
|
||||
public static native void generate36h11AprilTagImage(RawFrame frameObj, long frame, int id);
|
||||
|
||||
/** Utility class. */
|
||||
private AprilTagJNI() {}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "frc/apriltag/AprilTag.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <wpi/json.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -20,40 +22,33 @@
|
||||
|
||||
using namespace frc;
|
||||
|
||||
wpi::RawFrame AprilTag::Generate36h11AprilTagImage(int id) {
|
||||
apriltag_family_t* tagFamily = tag36h11_create();
|
||||
image_u8_t* image = apriltag_to_image(tagFamily, id);
|
||||
wpi::RawFrame markerFrame{};
|
||||
size_t totalDataSize = image->height * image->stride * sizeof(char);
|
||||
markerFrame.data = static_cast<char*>(
|
||||
std::calloc(image->height * image->stride, sizeof(char)));
|
||||
std::memcpy(markerFrame.data, image->buf, totalDataSize);
|
||||
markerFrame.dataLength = image->width;
|
||||
markerFrame.height = image->height;
|
||||
markerFrame.pixelFormat = WPI_PIXFMT_GRAY;
|
||||
markerFrame.width = image->stride;
|
||||
markerFrame.totalData = totalDataSize;
|
||||
static bool FamilyToImage(wpi::RawFrame* frame, apriltag_family_t* family,
|
||||
int id) {
|
||||
image_u8_t* image = apriltag_to_image(family, id);
|
||||
size_t totalDataSize = image->height * image->stride;
|
||||
bool rv = frame->Reserve(totalDataSize);
|
||||
std::memcpy(frame->data, image->buf, totalDataSize);
|
||||
frame->size = totalDataSize;
|
||||
frame->width = image->width;
|
||||
frame->height = image->height;
|
||||
frame->stride = image->stride;
|
||||
frame->pixelFormat = WPI_PIXFMT_GRAY;
|
||||
image_u8_destroy(image);
|
||||
tag36h11_destroy(tagFamily);
|
||||
return markerFrame;
|
||||
return rv;
|
||||
}
|
||||
|
||||
wpi::RawFrame AprilTag::Generate16h5AprilTagImage(int id) {
|
||||
bool AprilTag::Generate36h11AprilTagImage(wpi::RawFrame* frame, int id) {
|
||||
apriltag_family_t* tagFamily = tag36h11_create();
|
||||
bool rv = FamilyToImage(frame, tagFamily, id);
|
||||
tag36h11_destroy(tagFamily);
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool AprilTag::Generate16h5AprilTagImage(wpi::RawFrame* frame, int id) {
|
||||
apriltag_family_t* tagFamily = tag16h5_create();
|
||||
image_u8_t* image = apriltag_to_image(tagFamily, id);
|
||||
wpi::RawFrame markerFrame{};
|
||||
size_t totalDataSize = image->height * image->stride * sizeof(char);
|
||||
markerFrame.data = static_cast<char*>(
|
||||
std::calloc(image->height * image->stride, sizeof(char)));
|
||||
std::memcpy(markerFrame.data, image->buf, totalDataSize);
|
||||
markerFrame.dataLength = image->width;
|
||||
markerFrame.height = image->height;
|
||||
markerFrame.pixelFormat = WPI_PIXFMT_GRAY;
|
||||
markerFrame.width = image->stride;
|
||||
markerFrame.totalData = totalDataSize;
|
||||
image_u8_destroy(image);
|
||||
bool rv = FamilyToImage(frame, tagFamily, id);
|
||||
tag16h5_destroy(tagFamily);
|
||||
return markerFrame;
|
||||
return rv;
|
||||
}
|
||||
|
||||
void frc::to_json(wpi::json& json, const AprilTag& apriltag) {
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace frc {
|
||||
// C++ generated from resource files
|
||||
std::string_view GetResource_2022_rapidreact_json();
|
||||
std::string_view GetResource_2023_chargedup_json();
|
||||
std::string_view GetResource_2024_crescendo_json();
|
||||
|
||||
AprilTagFieldLayout LoadAprilTagLayoutField(AprilTagField field) {
|
||||
std::string_view fieldString;
|
||||
@@ -21,6 +22,9 @@ AprilTagFieldLayout LoadAprilTagLayoutField(AprilTagField field) {
|
||||
case AprilTagField::k2023ChargedUp:
|
||||
fieldString = GetResource_2023_chargedup_json();
|
||||
break;
|
||||
case AprilTagField::k2024Crescendo:
|
||||
fieldString = GetResource_2024_crescendo_json();
|
||||
break;
|
||||
case AprilTagField::kNumFields:
|
||||
throw std::invalid_argument("Invalid Field");
|
||||
}
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
// 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 <jni.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#define WPI_RAWFRAME_JNI
|
||||
#include <wpi/RawFrame.h>
|
||||
#include <wpi/jni_util.h>
|
||||
|
||||
#include "edu_wpi_first_apriltag_jni_AprilTagJNI.h"
|
||||
@@ -25,6 +29,7 @@ static JClass quaternionCls;
|
||||
static JClass rotation3dCls;
|
||||
static JClass transform3dCls;
|
||||
static JClass translation3dCls;
|
||||
static JClass rawFrameCls;
|
||||
static JException illegalArgEx;
|
||||
static JException nullPointerEx;
|
||||
|
||||
@@ -37,7 +42,8 @@ static const JClassInit classes[] = {
|
||||
{"edu/wpi/first/math/geometry/Quaternion", &quaternionCls},
|
||||
{"edu/wpi/first/math/geometry/Rotation3d", &rotation3dCls},
|
||||
{"edu/wpi/first/math/geometry/Transform3d", &transform3dCls},
|
||||
{"edu/wpi/first/math/geometry/Translation3d", &translation3dCls}};
|
||||
{"edu/wpi/first/math/geometry/Translation3d", &translation3dCls},
|
||||
{"edu/wpi/first/util/RawFrame", &rawFrameCls}};
|
||||
|
||||
static const JExceptionInit exceptions[] = {
|
||||
{"java/lang/IllegalArgumentException", &illegalArgEx},
|
||||
@@ -591,27 +597,38 @@ Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePose
|
||||
/*
|
||||
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||
* Method: generate16h5AprilTagImage
|
||||
* Signature: (IJ)V
|
||||
* Signature: (Ljava/lang/Object;JI)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_generate16h5AprilTagImage
|
||||
(JNIEnv* env, jclass, jint id, jlong framePtr)
|
||||
(JNIEnv* env, jclass, jobject frameObj, jlong framePtr, jint id)
|
||||
{
|
||||
wpi::RawFrame* javaRawFrame = (wpi::RawFrame*)framePtr;
|
||||
*javaRawFrame = AprilTag::Generate16h5AprilTagImage(id);
|
||||
auto* frame = reinterpret_cast<wpi::RawFrame*>(framePtr);
|
||||
if (!frame) {
|
||||
nullPointerEx.Throw(env, "frame is null");
|
||||
return;
|
||||
}
|
||||
bool newData = AprilTag::Generate16h5AprilTagImage(frame, id);
|
||||
wpi::SetFrameData(env, rawFrameCls, frameObj, *frame, newData);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||
* Method: generate36h11AprilTagImage
|
||||
* Signature: (IJ)V
|
||||
* Signature: (Ljava/lang/Object;JI)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_generate36h11AprilTagImage
|
||||
(JNIEnv* env, jclass, jint id, jlong framePtr)
|
||||
(JNIEnv* env, jclass, jobject frameObj, jlong framePtr, jint id)
|
||||
{
|
||||
wpi::RawFrame* javaRawFrame = (wpi::RawFrame*)framePtr;
|
||||
*javaRawFrame = AprilTag::Generate36h11AprilTagImage(id);
|
||||
auto* frame = reinterpret_cast<wpi::RawFrame*>(framePtr);
|
||||
if (!frame) {
|
||||
nullPointerEx.Throw(env, "frame is null");
|
||||
return;
|
||||
}
|
||||
// function might reallocate
|
||||
bool newData = AprilTag::Generate36h11AprilTagImage(frame, id);
|
||||
wpi::SetFrameData(env, rawFrameCls, frameObj, *frame, newData);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -12,18 +12,20 @@
|
||||
|
||||
namespace frc {
|
||||
|
||||
/**
|
||||
* Represents an AprilTag's metadata.
|
||||
*/
|
||||
struct WPILIB_DLLEXPORT AprilTag {
|
||||
/// The tag's ID.
|
||||
int ID;
|
||||
|
||||
/// The tag's pose.
|
||||
Pose3d pose;
|
||||
|
||||
/**
|
||||
* Checks equality between this AprilTag and another object.
|
||||
*/
|
||||
bool operator==(const AprilTag&) const = default;
|
||||
|
||||
static wpi::RawFrame Generate36h11AprilTagImage(int id);
|
||||
static wpi::RawFrame Generate16h5AprilTagImage(int id);
|
||||
static bool Generate36h11AprilTagImage(wpi::RawFrame* frame, int id);
|
||||
static bool Generate16h5AprilTagImage(wpi::RawFrame* frame, int id);
|
||||
};
|
||||
|
||||
WPILIB_DLLEXPORT
|
||||
|
||||
@@ -38,8 +38,13 @@ namespace frc {
|
||||
* towards the opposing alliance). */
|
||||
class WPILIB_DLLEXPORT AprilTagFieldLayout {
|
||||
public:
|
||||
/**
|
||||
* Common origin positions for the AprilTag coordinate system.
|
||||
*/
|
||||
enum class OriginPosition {
|
||||
/// Blue alliance wall, right side.
|
||||
kBlueAllianceWallRightSide,
|
||||
/// Red alliance wall, right side.
|
||||
kRedAllianceWallRightSide,
|
||||
};
|
||||
|
||||
|
||||
@@ -12,9 +12,16 @@
|
||||
|
||||
namespace frc {
|
||||
|
||||
/**
|
||||
* Loadable AprilTag field layouts.
|
||||
*/
|
||||
enum class AprilTagField {
|
||||
/// 2022 Rapid React.
|
||||
k2022RapidReact,
|
||||
/// 2023 Charged Up.
|
||||
k2023ChargedUp,
|
||||
/// 2024 Crescendo.
|
||||
k2024Crescendo,
|
||||
|
||||
// This is a placeholder for denoting the last supported field. This should
|
||||
// always be the last entry in the enum and should not be used by users
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
ID,X,Y,Z,Rotation
|
||||
1,593.68,9.68,53.38,120
|
||||
2,637.21,34.79,53.38,120
|
||||
3,652.73,196.17,57.13,180
|
||||
4,652.73,218.42,57.13,180
|
||||
5,578.77,323.00,53.38,270
|
||||
6,72.5,323.00,53.38,270
|
||||
7,-1.50,218.42,57.13,0
|
||||
8,-1.50,196.17,57.13,0
|
||||
9,14.02,34.79,53.38,60
|
||||
10,57.54,9.68,53.38,60
|
||||
11,468.69,146.19,52.00,300
|
||||
12,468.69,177.10,52.00,60
|
||||
13,441.74,161.62,52.00,180
|
||||
14,209.48,161.62,52.00,0
|
||||
15,182.73,177.10,52.00,120
|
||||
16,182.73,146.19,52.00,240
|
||||
|
@@ -0,0 +1,296 @@
|
||||
{
|
||||
"tags": [
|
||||
{
|
||||
"ID": 1,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 15.079471999999997,
|
||||
"y": 0.24587199999999998,
|
||||
"z": 1.355852
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.5000000000000001,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.8660254037844386
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 2,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.185134,
|
||||
"y": 0.883666,
|
||||
"z": 1.355852
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.5000000000000001,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.8660254037844386
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 3,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.579342,
|
||||
"y": 4.982717999999999,
|
||||
"z": 1.4511020000000001
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 4,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.579342,
|
||||
"y": 5.547867999999999,
|
||||
"z": 1.4511020000000001
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 5,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 14.700757999999999,
|
||||
"y": 8.2042,
|
||||
"z": 1.355852
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.7071067811865475,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 6,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 1.8415,
|
||||
"y": 8.2042,
|
||||
"z": 1.355852
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.7071067811865475,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 7,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": -0.038099999999999995,
|
||||
"y": 5.547867999999999,
|
||||
"z": 1.4511020000000001
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 8,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": -0.038099999999999995,
|
||||
"y": 4.982717999999999,
|
||||
"z": 1.4511020000000001
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 9,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 0.356108,
|
||||
"y": 0.883666,
|
||||
"z": 1.355852
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.8660254037844387,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.49999999999999994
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 10,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 1.4615159999999998,
|
||||
"y": 0.24587199999999998,
|
||||
"z": 1.355852
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.8660254037844387,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.49999999999999994
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 11,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.904726,
|
||||
"y": 3.7132259999999997,
|
||||
"z": 1.3208
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.8660254037844387,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.49999999999999994
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 12,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.904726,
|
||||
"y": 4.49834,
|
||||
"z": 1.3208
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.8660254037844387,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.49999999999999994
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 13,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.220196,
|
||||
"y": 4.105148,
|
||||
"z": 1.3208
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 14,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 5.320792,
|
||||
"y": 4.105148,
|
||||
"z": 1.3208
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 15,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.641342,
|
||||
"y": 4.49834,
|
||||
"z": 1.3208
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.5000000000000001,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.8660254037844386
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 16,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.641342,
|
||||
"y": 3.7132259999999997,
|
||||
"z": 1.3208
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.4999999999999998,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.8660254037844387
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"field": {
|
||||
"length": 16.451,
|
||||
"width": 8.211
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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.apriltag;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import edu.wpi.first.util.PixelFormat;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class AprilTagGenerationTest {
|
||||
@Test
|
||||
void test36h11() {
|
||||
var frame = AprilTag.generate36h11AprilTagImage(1);
|
||||
assertEquals(PixelFormat.kGray, frame.getPixelFormat());
|
||||
assertEquals(10, frame.getWidth());
|
||||
assertEquals(10, frame.getHeight());
|
||||
int stride = frame.getStride();
|
||||
assertEquals(stride * 10, frame.getSize());
|
||||
// check the diagonal values
|
||||
var data = frame.getData();
|
||||
assertEquals(-1, data.get(stride * 0 + 0)); // outer border is white
|
||||
assertEquals(0, data.get(stride * 1 + 1)); // inner border is black
|
||||
assertEquals(-1, data.get(stride * 2 + 2));
|
||||
assertEquals(-1, data.get(stride * 3 + 3));
|
||||
assertEquals(-1, data.get(stride * 4 + 4));
|
||||
assertEquals(0, data.get(stride * 5 + 5));
|
||||
assertEquals(0, data.get(stride * 6 + 6));
|
||||
assertEquals(-1, data.get(stride * 7 + 7));
|
||||
assertEquals(0, data.get(stride * 8 + 8)); // inner border
|
||||
assertEquals(-1, data.get(stride * 9 + 9)); // outer border
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import edu.wpi.first.cscore.VideoEvent;
|
||||
import edu.wpi.first.cscore.VideoException;
|
||||
import edu.wpi.first.cscore.VideoListener;
|
||||
import edu.wpi.first.cscore.VideoMode;
|
||||
import edu.wpi.first.cscore.VideoMode.PixelFormat;
|
||||
import edu.wpi.first.cscore.VideoSink;
|
||||
import edu.wpi.first.cscore.VideoSource;
|
||||
import edu.wpi.first.networktables.BooleanEntry;
|
||||
@@ -27,6 +26,7 @@ import edu.wpi.first.networktables.StringArrayPublisher;
|
||||
import edu.wpi.first.networktables.StringArrayTopic;
|
||||
import edu.wpi.first.networktables.StringEntry;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.util.PixelFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@@ -39,6 +39,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* NetworkTables.
|
||||
*/
|
||||
public final class CameraServer {
|
||||
/** CameraServer base port. */
|
||||
public static final int kBasePort = 1181;
|
||||
|
||||
private static final String kPublishName = "/CameraPublisher";
|
||||
@@ -686,7 +687,7 @@ public final class CameraServer {
|
||||
*/
|
||||
public static MjpegServer addSwitchedCamera(String name) {
|
||||
// create a dummy CvSource
|
||||
CvSource source = new CvSource(name, VideoMode.PixelFormat.kMJPEG, 160, 120, 30);
|
||||
CvSource source = new CvSource(name, PixelFormat.kMJPEG, 160, 120, 30);
|
||||
MjpegServer server = startAutomaticCapture(source);
|
||||
synchronized (CameraServer.class) {
|
||||
m_fixedSources.put(server.getHandle(), source.getHandle());
|
||||
@@ -801,7 +802,7 @@ public final class CameraServer {
|
||||
* @return OpenCV source for the MJPEG stream
|
||||
*/
|
||||
public static CvSource putVideo(String name, int width, int height) {
|
||||
CvSource source = new CvSource(name, VideoMode.PixelFormat.kMJPEG, width, height, 30);
|
||||
CvSource source = new CvSource(name, PixelFormat.kMJPEG, width, height, 30);
|
||||
startAutomaticCapture(source);
|
||||
return source;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
package edu.wpi.first.cameraserver;
|
||||
|
||||
/** CameraServer shared functions. */
|
||||
public interface CameraServerShared {
|
||||
/**
|
||||
* get the main thread id func.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
package edu.wpi.first.cameraserver;
|
||||
|
||||
/** Storage for CameraServerShared instance. */
|
||||
public final class CameraServerSharedStore {
|
||||
private static CameraServerShared cameraServerShared;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.opencv.core.Mat;
|
||||
* code. The easiest way to use this is to run it in a {@link VisionThread} and use the listener to
|
||||
* take snapshots of the pipeline's outputs.
|
||||
*
|
||||
* @param <P> Vision pipeline type.
|
||||
* @see VisionPipeline
|
||||
* @see VisionThread
|
||||
* @see <a href="package-summary.html">vision</a>
|
||||
|
||||
@@ -22,10 +22,8 @@ namespace frc {
|
||||
*/
|
||||
class CameraServer {
|
||||
public:
|
||||
/// CameraServer base port.
|
||||
static constexpr uint16_t kBasePort = 1181;
|
||||
static constexpr int kSize640x480 = 0;
|
||||
static constexpr int kSize320x240 = 1;
|
||||
static constexpr int kSize160x120 = 2;
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
macro(wpilib_target_warnings target)
|
||||
if(NOT MSVC)
|
||||
target_compile_options(
|
||||
${target}
|
||||
PRIVATE -Wall -pedantic -Wextra -Werror -Wno-unused-parameter ${WPILIB_TARGET_WARNINGS}
|
||||
set(WARNING_FLAGS
|
||||
-Wall
|
||||
-pedantic
|
||||
-Wextra
|
||||
-Wno-unused-parameter
|
||||
${WPILIB_TARGET_WARNINGS}
|
||||
)
|
||||
if(NOT NO_WERROR)
|
||||
set(WARNING_FLAGS ${WARNING_FLAGS} -Werror)
|
||||
endif()
|
||||
|
||||
target_compile_options(${target} PRIVATE ${WARNING_FLAGS})
|
||||
else()
|
||||
target_compile_options(
|
||||
${target}
|
||||
|
||||
@@ -9,21 +9,36 @@ import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.opencv.core.Core;
|
||||
|
||||
/** CameraServer CV JNI. */
|
||||
public class CameraServerCvJNI {
|
||||
static boolean libraryLoaded = false;
|
||||
|
||||
static RuntimeLoader<Core> loader = null;
|
||||
|
||||
/** Sets whether JNI should be loaded in the static block. */
|
||||
public static class Helper {
|
||||
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
|
||||
|
||||
/**
|
||||
* Returns true if the JNI should be loaded in the static block.
|
||||
*
|
||||
* @return True if the JNI should be loaded in the static block.
|
||||
*/
|
||||
public static boolean getExtractOnStaticLoad() {
|
||||
return extractOnStaticLoad.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the JNI should be loaded in the static block.
|
||||
*
|
||||
* @param load Whether the JNI should be loaded in the static block.
|
||||
*/
|
||||
public static void setExtractOnStaticLoad(boolean load) {
|
||||
extractOnStaticLoad.set(load);
|
||||
}
|
||||
|
||||
/** Utility class. */
|
||||
private Helper() {}
|
||||
}
|
||||
|
||||
static {
|
||||
@@ -72,4 +87,7 @@ public class CameraServerCvJNI {
|
||||
public static native long grabSinkFrame(int sink, long imageNativeObj);
|
||||
|
||||
public static native long grabSinkFrameTimeout(int sink, long imageNativeObj, double timeout);
|
||||
|
||||
/** Utility class. */
|
||||
private CameraServerCvJNI() {}
|
||||
}
|
||||
|
||||
@@ -11,21 +11,36 @@ import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/** CameraServer JNI. */
|
||||
public class CameraServerJNI {
|
||||
static boolean libraryLoaded = false;
|
||||
|
||||
static RuntimeLoader<CameraServerJNI> loader = null;
|
||||
|
||||
/** Sets whether JNI should be loaded in the static block. */
|
||||
public static class Helper {
|
||||
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
|
||||
|
||||
/**
|
||||
* Returns true if the JNI should be loaded in the static block.
|
||||
*
|
||||
* @return True if the JNI should be loaded in the static block.
|
||||
*/
|
||||
public static boolean getExtractOnStaticLoad() {
|
||||
return extractOnStaticLoad.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the JNI should be loaded in the static block.
|
||||
*
|
||||
* @param load Whether the JNI should be loaded in the static block.
|
||||
*/
|
||||
public static void setExtractOnStaticLoad(boolean load) {
|
||||
extractOnStaticLoad.set(load);
|
||||
}
|
||||
|
||||
/** Utility class. */
|
||||
private Helper() {}
|
||||
}
|
||||
|
||||
static {
|
||||
@@ -182,21 +197,13 @@ public class CameraServerJNI {
|
||||
//
|
||||
// Image Source Functions
|
||||
//
|
||||
public static native void putRawSourceFrame(int source, long frame);
|
||||
|
||||
public static native void putRawSourceFrameBB(
|
||||
int source, ByteBuffer data, int width, int height, int pixelFormat, int totalData);
|
||||
int source, ByteBuffer data, int size, int width, int height, int stride, int pixelFormat);
|
||||
|
||||
public static native void putRawSourceFrame(
|
||||
int source, long data, int width, int height, int pixelFormat, int totalData);
|
||||
|
||||
public static void putRawSourceFrame(int source, RawFrame raw) {
|
||||
putRawSourceFrame(
|
||||
source,
|
||||
raw.getDataPtr(),
|
||||
raw.getWidth(),
|
||||
raw.getHeight(),
|
||||
raw.getPixelFormat(),
|
||||
raw.getTotalData());
|
||||
}
|
||||
public static native void putRawSourceFrameData(
|
||||
int source, long data, int size, int width, int height, int stride, int pixelFormat);
|
||||
|
||||
public static native void notifySourceError(int source, String msg);
|
||||
|
||||
@@ -263,47 +270,10 @@ public class CameraServerJNI {
|
||||
//
|
||||
public static native void setSinkDescription(int sink, String description);
|
||||
|
||||
private static native long grabRawSinkFrameImpl(
|
||||
int sink,
|
||||
RawFrame rawFrame,
|
||||
long rawFramePtr,
|
||||
ByteBuffer byteBuffer,
|
||||
int width,
|
||||
int height,
|
||||
int pixelFormat);
|
||||
public static native long grabRawSinkFrame(int sink, RawFrame frame, long nativeObj);
|
||||
|
||||
private static native long grabRawSinkFrameTimeoutImpl(
|
||||
int sink,
|
||||
RawFrame rawFrame,
|
||||
long rawFramePtr,
|
||||
ByteBuffer byteBuffer,
|
||||
int width,
|
||||
int height,
|
||||
int pixelFormat,
|
||||
double timeout);
|
||||
|
||||
public static long grabSinkFrame(int sink, RawFrame rawFrame) {
|
||||
return grabRawSinkFrameImpl(
|
||||
sink,
|
||||
rawFrame,
|
||||
rawFrame.getFramePtr(),
|
||||
rawFrame.getDataByteBuffer(),
|
||||
rawFrame.getWidth(),
|
||||
rawFrame.getHeight(),
|
||||
rawFrame.getPixelFormat());
|
||||
}
|
||||
|
||||
public static long grabSinkFrameTimeout(int sink, RawFrame rawFrame, double timeout) {
|
||||
return grabRawSinkFrameTimeoutImpl(
|
||||
sink,
|
||||
rawFrame,
|
||||
rawFrame.getFramePtr(),
|
||||
rawFrame.getDataByteBuffer(),
|
||||
rawFrame.getWidth(),
|
||||
rawFrame.getHeight(),
|
||||
rawFrame.getPixelFormat(),
|
||||
timeout);
|
||||
}
|
||||
public static native long grabRawSinkFrameTimeout(
|
||||
int sink, RawFrame frame, long nativeObj, double timeout);
|
||||
|
||||
public static native String getSinkError(int sink);
|
||||
|
||||
@@ -334,7 +304,9 @@ public class CameraServerJNI {
|
||||
// Telemetry Functions
|
||||
//
|
||||
public enum TelemetryKind {
|
||||
/** kSourceBytesReceived. */
|
||||
kSourceBytesReceived(1),
|
||||
/** kSourceFramesReceived. */
|
||||
kSourceFramesReceived(2);
|
||||
|
||||
private final int value;
|
||||
@@ -392,4 +364,7 @@ public class CameraServerJNI {
|
||||
public static native int runMainRunLoopTimeout(double timeoutSeconds);
|
||||
|
||||
public static native void stopMainRunLoop();
|
||||
|
||||
/** Utility class. */
|
||||
private CameraServerJNI() {}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
package edu.wpi.first.cscore;
|
||||
|
||||
import edu.wpi.first.cscore.VideoMode.PixelFormat;
|
||||
import edu.wpi.first.util.PixelFormat;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
package edu.wpi.first.cscore;
|
||||
|
||||
import edu.wpi.first.util.PixelFormat;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
/**
|
||||
@@ -32,7 +33,7 @@ public class CvSource extends ImageSource {
|
||||
* @param height height
|
||||
* @param fps fps
|
||||
*/
|
||||
public CvSource(String name, VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
|
||||
public CvSource(String name, PixelFormat pixelFormat, int width, int height, int fps) {
|
||||
super(CameraServerCvJNI.createCvSource(name, pixelFormat.getValue(), width, height, fps));
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,13 @@ package edu.wpi.first.cscore;
|
||||
/** A source that represents a MJPEG-over-HTTP (IP) camera. */
|
||||
public class HttpCamera extends VideoCamera {
|
||||
public enum HttpCameraKind {
|
||||
/** Unknown camera kind. */
|
||||
kUnknown(0),
|
||||
/** MJPG Streamer camera. */
|
||||
kMJPGStreamer(1),
|
||||
/** CS Core camera. */
|
||||
kCSCore(2),
|
||||
/** Axis camera. */
|
||||
kAxis(3);
|
||||
|
||||
private final int value;
|
||||
|
||||
@@ -12,6 +12,9 @@ public class VideoCamera extends VideoSource {
|
||||
public static final int kFixedOutdoor2 = 5000;
|
||||
public static final int kFixedFluorescent1 = 5100;
|
||||
public static final int kFixedFlourescent2 = 5200;
|
||||
|
||||
/** Default constructor. */
|
||||
public WhiteBalance() {}
|
||||
}
|
||||
|
||||
protected VideoCamera(int handle) {
|
||||
|
||||
@@ -8,26 +8,47 @@ package edu.wpi.first.cscore;
|
||||
@SuppressWarnings("MemberName")
|
||||
public class VideoEvent {
|
||||
public enum Kind {
|
||||
/** Unknown video event. */
|
||||
kUnknown(0x0000),
|
||||
/** Source Created event. */
|
||||
kSourceCreated(0x0001),
|
||||
/** Source Destroyed event. */
|
||||
kSourceDestroyed(0x0002),
|
||||
/** Source Connected event. */
|
||||
kSourceConnected(0x0004),
|
||||
/** Source Disconnected event. */
|
||||
kSourceDisconnected(0x0008),
|
||||
/** Source Video Modes Updated event. */
|
||||
kSourceVideoModesUpdated(0x0010),
|
||||
/** Source VideoMode Changed event. */
|
||||
kSourceVideoModeChanged(0x0020),
|
||||
/** Source Property Created event. */
|
||||
kSourcePropertyCreated(0x0040),
|
||||
/** Source Property Value Updated event. */
|
||||
kSourcePropertyValueUpdated(0x0080),
|
||||
/** Source Property Choices Updated event. */
|
||||
kSourcePropertyChoicesUpdated(0x0100),
|
||||
/** Sink Source Changed event. */
|
||||
kSinkSourceChanged(0x0200),
|
||||
/** Sink Created event. */
|
||||
kSinkCreated(0x0400),
|
||||
/** Sink Destroyed event. */
|
||||
kSinkDestroyed(0x0800),
|
||||
/** Sink Enabled event. */
|
||||
kSinkEnabled(0x1000),
|
||||
/** Sink Disabled event. */
|
||||
kSinkDisabled(0x2000),
|
||||
/** Network Interfaces Changed event. */
|
||||
kNetworkInterfacesChanged(0x4000),
|
||||
/** Telemetry Updated event. */
|
||||
kTelemetryUpdated(0x8000),
|
||||
/** Sink Property Created event. */
|
||||
kSinkPropertyCreated(0x10000),
|
||||
/** Sink Property Value Updated event. */
|
||||
kSinkPropertyValueUpdated(0x20000),
|
||||
/** Sink Property Choices Updated event. */
|
||||
kSinkPropertyChoicesUpdated(0x40000),
|
||||
/** Usb Cameras Changed event. */
|
||||
kUsbCamerasChanged(0x80000);
|
||||
|
||||
private final int value;
|
||||
|
||||
@@ -4,38 +4,12 @@
|
||||
|
||||
package edu.wpi.first.cscore;
|
||||
|
||||
import edu.wpi.first.util.PixelFormat;
|
||||
import java.util.Objects;
|
||||
|
||||
/** Video mode. */
|
||||
@SuppressWarnings("MemberName")
|
||||
public class VideoMode {
|
||||
public enum PixelFormat {
|
||||
kUnknown(0),
|
||||
kMJPEG(1),
|
||||
kYUYV(2),
|
||||
kRGB565(3),
|
||||
kBGR(4),
|
||||
kGray(5),
|
||||
kY16(6),
|
||||
kUYVY(7);
|
||||
|
||||
private final int value;
|
||||
|
||||
PixelFormat(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private static final PixelFormat[] m_pixelFormatValues = PixelFormat.values();
|
||||
|
||||
public static PixelFormat getPixelFormatFromInt(int pixelFormat) {
|
||||
return m_pixelFormatValues[pixelFormat];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new video mode.
|
||||
*
|
||||
@@ -45,7 +19,7 @@ public class VideoMode {
|
||||
* @param fps The camera's frames per second.
|
||||
*/
|
||||
public VideoMode(int pixelFormat, int width, int height, int fps) {
|
||||
this.pixelFormat = getPixelFormatFromInt(pixelFormat);
|
||||
this.pixelFormat = PixelFormat.getFromInt(pixelFormat);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.fps = fps;
|
||||
|
||||
@@ -6,11 +6,17 @@ package edu.wpi.first.cscore;
|
||||
|
||||
/** A source or sink property. */
|
||||
public class VideoProperty {
|
||||
/** VideoProperty property types. */
|
||||
public enum Kind {
|
||||
/** No specific property. */
|
||||
kNone(0),
|
||||
/** Boolean property. */
|
||||
kBoolean(1),
|
||||
/** Integer property. */
|
||||
kInteger(2),
|
||||
/** String property. */
|
||||
kString(4),
|
||||
/** Enum property. */
|
||||
kEnum(8);
|
||||
|
||||
private final int value;
|
||||
|
||||
@@ -9,10 +9,15 @@ package edu.wpi.first.cscore;
|
||||
* (e.g. from a stereo or depth camera); these are called channels.
|
||||
*/
|
||||
public class VideoSink implements AutoCloseable {
|
||||
/** Video sink types. */
|
||||
public enum Kind {
|
||||
/** Unknown video sink type. */
|
||||
kUnknown(0),
|
||||
/** MJPEG video sink. */
|
||||
kMjpeg(2),
|
||||
/** CV video sink. */
|
||||
kCv(4),
|
||||
/** Raw video sink. */
|
||||
kRaw(8);
|
||||
|
||||
private final int value;
|
||||
|
||||
@@ -4,16 +4,23 @@
|
||||
|
||||
package edu.wpi.first.cscore;
|
||||
|
||||
import edu.wpi.first.util.PixelFormat;
|
||||
|
||||
/**
|
||||
* A source for video that provides a sequence of frames. Each frame may consist of multiple images
|
||||
* (e.g. from a stereo or depth camera); these are called channels.
|
||||
*/
|
||||
public class VideoSource implements AutoCloseable {
|
||||
public enum Kind {
|
||||
/** Unknown video source. */
|
||||
kUnknown(0),
|
||||
/** USB video source. */
|
||||
kUsb(1),
|
||||
/** HTTP video source. */
|
||||
kHttp(2),
|
||||
/** CV video source. */
|
||||
kCv(4),
|
||||
/** Raw video source. */
|
||||
kRaw(8);
|
||||
|
||||
private final int value;
|
||||
@@ -235,7 +242,7 @@ public class VideoSource implements AutoCloseable {
|
||||
* @param fps desired FPS
|
||||
* @return True if set successfully
|
||||
*/
|
||||
public boolean setVideoMode(VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
|
||||
public boolean setVideoMode(PixelFormat pixelFormat, int width, int height, int fps) {
|
||||
return CameraServerJNI.setSourceVideoMode(m_handle, pixelFormat.getValue(), width, height, fps);
|
||||
}
|
||||
|
||||
@@ -245,7 +252,7 @@ public class VideoSource implements AutoCloseable {
|
||||
* @param pixelFormat desired pixel format
|
||||
* @return True if set successfully
|
||||
*/
|
||||
public boolean setPixelFormat(VideoMode.PixelFormat pixelFormat) {
|
||||
public boolean setPixelFormat(PixelFormat pixelFormat) {
|
||||
return CameraServerJNI.setSourcePixelFormat(m_handle, pixelFormat.getValue());
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ public class RawSink extends ImageSink {
|
||||
* @return Frame time, or 0 on error (call getError() to obtain the error message); the frame time
|
||||
* is in the same time base as wpi::Now(), and is in 1 us increments.
|
||||
*/
|
||||
protected long grabFrame(RawFrame frame) {
|
||||
public long grabFrame(RawFrame frame) {
|
||||
return grabFrame(frame, 0.225);
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ public class RawSink extends ImageSink {
|
||||
* @return Frame time, or 0 on error (call getError() to obtain the error message); the frame time
|
||||
* is in the same time base as wpi::Now(), and is in 1 us increments.
|
||||
*/
|
||||
protected long grabFrame(RawFrame frame, double timeout) {
|
||||
return CameraServerJNI.grabSinkFrameTimeout(m_handle, frame, timeout);
|
||||
public long grabFrame(RawFrame frame, double timeout) {
|
||||
return CameraServerJNI.grabRawSinkFrameTimeout(m_handle, frame, frame.getNativeObj(), timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,7 +58,7 @@ public class RawSink extends ImageSink {
|
||||
* @return Frame time, or 0 on error (call getError() to obtain the error message); the frame time
|
||||
* is in the same time base as wpi::Now(), and is in 1 us increments.
|
||||
*/
|
||||
protected long grabFrameNoTimeout(RawFrame frame) {
|
||||
return CameraServerJNI.grabSinkFrame(m_handle, frame);
|
||||
public long grabFrameNoTimeout(RawFrame frame) {
|
||||
return CameraServerJNI.grabRawSinkFrame(m_handle, frame, frame.getNativeObj());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ package edu.wpi.first.cscore.raw;
|
||||
import edu.wpi.first.cscore.CameraServerJNI;
|
||||
import edu.wpi.first.cscore.ImageSource;
|
||||
import edu.wpi.first.cscore.VideoMode;
|
||||
import edu.wpi.first.util.PixelFormat;
|
||||
import edu.wpi.first.util.RawFrame;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* A source for user code to provide video frames as raw bytes.
|
||||
@@ -36,7 +38,7 @@ public class RawSource extends ImageSource {
|
||||
* @param height height
|
||||
* @param fps fps
|
||||
*/
|
||||
public RawSource(String name, VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
|
||||
public RawSource(String name, PixelFormat pixelFormat, int width, int height, int fps) {
|
||||
super(CameraServerJNI.createRawSource(name, pixelFormat.getValue(), width, height, fps));
|
||||
}
|
||||
|
||||
@@ -45,35 +47,41 @@ public class RawSource extends ImageSource {
|
||||
*
|
||||
* @param image raw frame image
|
||||
*/
|
||||
protected void putFrame(RawFrame image) {
|
||||
CameraServerJNI.putRawSourceFrame(m_handle, image);
|
||||
public void putFrame(RawFrame image) {
|
||||
CameraServerJNI.putRawSourceFrame(m_handle, image.getNativeObj());
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a raw image and notify sinks.
|
||||
*
|
||||
* @param data raw frame data pointer
|
||||
* @param data raw frame native data pointer
|
||||
* @param size total size in bytes
|
||||
* @param width frame width
|
||||
* @param height frame height
|
||||
* @param stride size of each row in bytes
|
||||
* @param pixelFormat pixel format
|
||||
* @param totalData length of data in total
|
||||
*/
|
||||
protected void putFrame(long data, int width, int height, int pixelFormat, int totalData) {
|
||||
CameraServerJNI.putRawSourceFrame(m_handle, data, width, height, pixelFormat, totalData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a raw image and notify sinks.
|
||||
*
|
||||
* @param data raw frame data pointer
|
||||
* @param width frame width
|
||||
* @param height frame height
|
||||
* @param pixelFormat pixel format
|
||||
* @param totalData length of data in total
|
||||
*/
|
||||
protected void putFrame(
|
||||
long data, int width, int height, VideoMode.PixelFormat pixelFormat, int totalData) {
|
||||
CameraServerJNI.putRawSourceFrame(
|
||||
m_handle, data, width, height, pixelFormat.getValue(), totalData);
|
||||
long data, int size, int width, int height, int stride, PixelFormat pixelFormat) {
|
||||
CameraServerJNI.putRawSourceFrameData(
|
||||
m_handle, data, size, width, height, stride, pixelFormat.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a raw image and notify sinks.
|
||||
*
|
||||
* @param data raw frame native ByteBuffer
|
||||
* @param width frame width
|
||||
* @param height frame height
|
||||
* @param stride size of each row in bytes
|
||||
* @param pixelFormat pixel format
|
||||
*/
|
||||
public void putFrame(
|
||||
ByteBuffer data, int width, int height, int stride, PixelFormat pixelFormat) {
|
||||
if (!data.isDirect()) {
|
||||
throw new UnsupportedOperationException("ByteBuffer must be direct");
|
||||
}
|
||||
CameraServerJNI.putRawSourceFrameBB(
|
||||
m_handle, data, data.limit(), width, height, stride, pixelFormat.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,23 @@ class Image {
|
||||
return cv::Mat{height, width, type, m_data.data()};
|
||||
}
|
||||
|
||||
int GetStride() const {
|
||||
switch (pixelFormat) {
|
||||
case VideoMode::kYUYV:
|
||||
case VideoMode::kRGB565:
|
||||
case VideoMode::kY16:
|
||||
case VideoMode::kUYVY:
|
||||
return 2 * width;
|
||||
case VideoMode::kBGR:
|
||||
return 3 * width;
|
||||
case VideoMode::kGray:
|
||||
return width;
|
||||
case VideoMode::kMJPEG:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
cv::_InputArray AsInputArray() { return cv::_InputArray{m_data}; }
|
||||
|
||||
bool Is(int width_, int height_) {
|
||||
|
||||
@@ -109,10 +109,10 @@ uint64_t RawSinkImpl::GrabFrameImpl(WPI_RawFrame& rawFrame,
|
||||
WPI_AllocateRawFrameData(&rawFrame, newImage->size());
|
||||
rawFrame.height = newImage->height;
|
||||
rawFrame.width = newImage->width;
|
||||
rawFrame.stride = newImage->GetStride();
|
||||
rawFrame.pixelFormat = newImage->pixelFormat;
|
||||
rawFrame.totalData = newImage->size();
|
||||
std::copy(newImage->data(), newImage->data() + rawFrame.totalData,
|
||||
rawFrame.data);
|
||||
rawFrame.size = newImage->size();
|
||||
std::copy(newImage->data(), newImage->data() + rawFrame.size, rawFrame.data);
|
||||
|
||||
return incomingFrame.GetTime();
|
||||
}
|
||||
|
||||
@@ -39,10 +39,11 @@ void RawSourceImpl::PutFrame(const WPI_RawFrame& image) {
|
||||
type = CV_8UC1;
|
||||
break;
|
||||
}
|
||||
cv::Mat finalImage{image.height, image.width, type, image.data};
|
||||
cv::Mat finalImage{image.height, image.width, type, image.data,
|
||||
static_cast<size_t>(image.stride)};
|
||||
std::unique_ptr<Image> dest =
|
||||
AllocImage(static_cast<VideoMode::PixelFormat>(image.pixelFormat),
|
||||
image.width, image.height, image.totalData);
|
||||
image.width, image.height, image.size);
|
||||
finalImage.copyTo(dest->AsMat());
|
||||
|
||||
SourceImpl::PutFrame(std::move(dest), wpi::Now());
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <opencv2/core/core.hpp>
|
||||
|
||||
#define WPI_RAWFRAME_JNI
|
||||
#include <wpi/RawFrame.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/jni_util.h>
|
||||
|
||||
@@ -42,7 +45,8 @@ static JNIEnv* listenerEnv = nullptr;
|
||||
static const JClassInit classes[] = {
|
||||
{"edu/wpi/first/cscore/UsbCameraInfo", &usbCameraInfoCls},
|
||||
{"edu/wpi/first/cscore/VideoMode", &videoModeCls},
|
||||
{"edu/wpi/first/cscore/VideoEvent", &videoEventCls}};
|
||||
{"edu/wpi/first/cscore/VideoEvent", &videoEventCls},
|
||||
{"edu/wpi/first/util/RawFrame", &rawFrameCls}};
|
||||
|
||||
static const JExceptionInit exceptions[] = {
|
||||
{"edu/wpi/first/cscore/VideoException", &videoEx},
|
||||
@@ -1219,48 +1223,78 @@ Java_edu_wpi_first_cscore_CameraServerCvJNI_putSourceFrame
|
||||
}
|
||||
}
|
||||
|
||||
// int width, int height, int pixelFormat, int totalData
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerJNI
|
||||
* Method: putRawSourceFrameBB
|
||||
* Signature: (ILjava/lang/Object;IIII)V
|
||||
* Method: putRawSourceFrame
|
||||
* Signature: (IJ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrameBB
|
||||
(JNIEnv* env, jclass, jint source, jobject byteBuffer, jint width,
|
||||
jint height, jint pixelFormat, jint totalData)
|
||||
Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrame
|
||||
(JNIEnv* env, jclass, jint source, jlong framePtr)
|
||||
{
|
||||
WPI_RawFrame rawFrame;
|
||||
rawFrame.data =
|
||||
reinterpret_cast<char*>(env->GetDirectBufferAddress(byteBuffer));
|
||||
rawFrame.totalData = totalData;
|
||||
rawFrame.pixelFormat = pixelFormat;
|
||||
rawFrame.width = width;
|
||||
rawFrame.height = height;
|
||||
auto* frame = reinterpret_cast<wpi::RawFrame*>(framePtr);
|
||||
if (!frame) {
|
||||
nullPointerEx.Throw(env, "frame is null");
|
||||
return;
|
||||
}
|
||||
CS_Status status = 0;
|
||||
cs::PutSourceFrame(source, rawFrame, &status);
|
||||
cs::PutSourceFrame(source, *frame, &status);
|
||||
CheckStatus(env, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerJNI
|
||||
* Method: putRawSourceFrame
|
||||
* Signature: (IJIIII)V
|
||||
* Method: putRawSourceFrameBB
|
||||
* Signature: (ILjava/lang/Object;IIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrame
|
||||
(JNIEnv* env, jclass, jint source, jlong ptr, jint width, jint height,
|
||||
jint pixelFormat, jint totalData)
|
||||
Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrameBB
|
||||
(JNIEnv* env, jclass, jint source, jobject data, jint size, jint width,
|
||||
jint height, jint stride, jint pixelFormat)
|
||||
{
|
||||
WPI_RawFrame rawFrame;
|
||||
rawFrame.data = reinterpret_cast<char*>(static_cast<intptr_t>(ptr));
|
||||
rawFrame.totalData = totalData;
|
||||
rawFrame.pixelFormat = pixelFormat;
|
||||
rawFrame.width = width;
|
||||
rawFrame.height = height;
|
||||
WPI_RawFrame frame; // use WPI_Frame because we don't want the destructor
|
||||
frame.data = static_cast<uint8_t*>(env->GetDirectBufferAddress(data));
|
||||
if (!frame.data) {
|
||||
nullPointerEx.Throw(env, "data is null");
|
||||
return;
|
||||
}
|
||||
frame.freeFunc = nullptr;
|
||||
frame.freeCbData = nullptr;
|
||||
frame.size = size;
|
||||
frame.width = width;
|
||||
frame.height = height;
|
||||
frame.stride = stride;
|
||||
frame.pixelFormat = pixelFormat;
|
||||
CS_Status status = 0;
|
||||
cs::PutSourceFrame(source, rawFrame, &status);
|
||||
cs::PutSourceFrame(source, frame, &status);
|
||||
CheckStatus(env, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerJNI
|
||||
* Method: putRawSourceFrameData
|
||||
* Signature: (IJIIIII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrameData
|
||||
(JNIEnv* env, jclass, jint source, jlong data, jint size, jint width,
|
||||
jint height, jint stride, jint pixelFormat)
|
||||
{
|
||||
WPI_RawFrame frame; // use WPI_Frame because we don't want the destructor
|
||||
frame.data = reinterpret_cast<uint8_t*>(data);
|
||||
if (!frame.data) {
|
||||
nullPointerEx.Throw(env, "data is null");
|
||||
return;
|
||||
}
|
||||
frame.freeFunc = nullptr;
|
||||
frame.freeCbData = nullptr;
|
||||
frame.size = size;
|
||||
frame.width = width;
|
||||
frame.height = height;
|
||||
frame.stride = stride;
|
||||
frame.pixelFormat = pixelFormat;
|
||||
CS_Status status = 0;
|
||||
cs::PutSourceFrame(source, frame, &status);
|
||||
CheckStatus(env, status);
|
||||
}
|
||||
|
||||
@@ -1721,72 +1755,47 @@ Java_edu_wpi_first_cscore_CameraServerCvJNI_grabSinkFrameTimeout
|
||||
}
|
||||
}
|
||||
|
||||
static void SetRawFrameData(JNIEnv* env, jobject rawFrameObj,
|
||||
jobject byteBuffer, bool didChangeDataPtr,
|
||||
const WPI_RawFrame& frame) {
|
||||
static jmethodID setMethod =
|
||||
env->GetMethodID(rawFrameCls, "setData", "(Ljava/nio/ByteBuffer;JIIII)V");
|
||||
jlong framePtr = static_cast<jlong>(reinterpret_cast<intptr_t>(frame.data));
|
||||
|
||||
if (didChangeDataPtr) {
|
||||
byteBuffer = env->NewDirectByteBuffer(frame.data, frame.dataLength);
|
||||
}
|
||||
|
||||
env->CallVoidMethod(
|
||||
rawFrameObj, setMethod, byteBuffer, framePtr,
|
||||
static_cast<jint>(frame.totalData), static_cast<jint>(frame.width),
|
||||
static_cast<jint>(frame.height), static_cast<jint>(frame.pixelFormat));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerJNI
|
||||
* Method: grabRawSinkFrameImpl
|
||||
* Signature: (ILjava/lang/Object;JLjava/lang/Object;III)J
|
||||
* Method: grabRawSinkFrame
|
||||
* Signature: (ILjava/lang/Object;J)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_edu_wpi_first_cscore_CameraServerJNI_grabRawSinkFrameImpl
|
||||
(JNIEnv* env, jclass, jint sink, jobject rawFrameObj, jlong rawFramePtr,
|
||||
jobject byteBuffer, jint width, jint height, jint pixelFormat)
|
||||
Java_edu_wpi_first_cscore_CameraServerJNI_grabRawSinkFrame
|
||||
(JNIEnv* env, jclass, jint sink, jobject frameObj, jlong framePtr)
|
||||
{
|
||||
WPI_RawFrame* ptr =
|
||||
reinterpret_cast<WPI_RawFrame*>(static_cast<intptr_t>(rawFramePtr));
|
||||
auto origDataPtr = ptr->data;
|
||||
ptr->width = width;
|
||||
ptr->height = height;
|
||||
ptr->pixelFormat = pixelFormat;
|
||||
auto* frame = reinterpret_cast<wpi::RawFrame*>(framePtr);
|
||||
auto origData = frame->data;
|
||||
CS_Status status = 0;
|
||||
auto rv = cs::GrabSinkFrame(static_cast<CS_Sink>(sink), *ptr, &status);
|
||||
auto rv = cs::GrabSinkFrame(static_cast<CS_Sink>(sink), *frame, &status);
|
||||
if (!CheckStatus(env, status)) {
|
||||
return 0;
|
||||
}
|
||||
SetRawFrameData(env, rawFrameObj, byteBuffer, origDataPtr != ptr->data, *ptr);
|
||||
wpi::SetFrameData(env, rawFrameCls, frameObj, *frame,
|
||||
origData != frame->data);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerJNI
|
||||
* Method: grabRawSinkFrameTimeoutImpl
|
||||
* Signature: (ILjava/lang/Object;JLjava/lang/Object;IIID)J
|
||||
* Method: grabRawSinkFrameTimeout
|
||||
* Signature: (ILjava/lang/Object;JD)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_edu_wpi_first_cscore_CameraServerJNI_grabRawSinkFrameTimeoutImpl
|
||||
(JNIEnv* env, jclass, jint sink, jobject rawFrameObj, jlong rawFramePtr,
|
||||
jobject byteBuffer, jint width, jint height, jint pixelFormat,
|
||||
Java_edu_wpi_first_cscore_CameraServerJNI_grabRawSinkFrameTimeout
|
||||
(JNIEnv* env, jclass, jint sink, jobject frameObj, jlong framePtr,
|
||||
jdouble timeout)
|
||||
{
|
||||
WPI_RawFrame* ptr =
|
||||
reinterpret_cast<WPI_RawFrame*>(static_cast<intptr_t>(rawFramePtr));
|
||||
auto origDataPtr = ptr->data;
|
||||
ptr->width = width;
|
||||
ptr->height = height;
|
||||
ptr->pixelFormat = pixelFormat;
|
||||
auto* frame = reinterpret_cast<wpi::RawFrame*>(framePtr);
|
||||
auto origData = frame->data;
|
||||
CS_Status status = 0;
|
||||
auto rv = cs::GrabSinkFrameTimeout(static_cast<CS_Sink>(sink), *ptr, timeout,
|
||||
&status);
|
||||
auto rv = cs::GrabSinkFrameTimeout(static_cast<CS_Sink>(sink), *frame,
|
||||
timeout, &status);
|
||||
if (!CheckStatus(env, status)) {
|
||||
return 0;
|
||||
}
|
||||
SetRawFrameData(env, rawFrameObj, byteBuffer, origDataPtr != ptr->data, *ptr);
|
||||
wpi::SetFrameData(env, rawFrameCls, frameObj, *frame,
|
||||
origData != frame->data);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,10 +44,15 @@ class VideoProperty {
|
||||
|
||||
public:
|
||||
enum Kind {
|
||||
/// No specific property.
|
||||
kNone = CS_PROP_NONE,
|
||||
/// Boolean property.
|
||||
kBoolean = CS_PROP_BOOLEAN,
|
||||
/// Integer property.
|
||||
kInteger = CS_PROP_INTEGER,
|
||||
/// String property.
|
||||
kString = CS_PROP_STRING,
|
||||
/// Enum property.
|
||||
kEnum = CS_PROP_ENUM
|
||||
};
|
||||
|
||||
@@ -100,9 +105,13 @@ class VideoSource {
|
||||
|
||||
public:
|
||||
enum Kind {
|
||||
/// Unknown video source.
|
||||
kUnknown = CS_SOURCE_UNKNOWN,
|
||||
/// USB video source.
|
||||
kUsb = CS_SOURCE_USB,
|
||||
/// HTTP video source.
|
||||
kHttp = CS_SOURCE_HTTP,
|
||||
/// CV video source.
|
||||
kCv = CS_SOURCE_CV
|
||||
};
|
||||
|
||||
@@ -471,9 +480,13 @@ class UsbCamera : public VideoCamera {
|
||||
class HttpCamera : public VideoCamera {
|
||||
public:
|
||||
enum HttpCameraKind {
|
||||
/// Unknown camera kind.
|
||||
kUnknown = CS_HTTP_UNKNOWN,
|
||||
/// MJPG Streamer camera.
|
||||
kMJPGStreamer = CS_HTTP_MJPGSTREAMER,
|
||||
/// CS Core camera.
|
||||
kCSCore = CS_HTTP_CSCORE,
|
||||
/// Axis camera.
|
||||
kAxis = CS_HTTP_AXIS
|
||||
};
|
||||
|
||||
@@ -716,8 +729,11 @@ class VideoSink {
|
||||
|
||||
public:
|
||||
enum Kind {
|
||||
/// Unknown sink type.
|
||||
kUnknown = CS_SINK_UNKNOWN,
|
||||
/// MJPEG video sink.
|
||||
kMjpeg = CS_SINK_MJPEG,
|
||||
/// CV video sink.
|
||||
kCv = CS_SINK_CV
|
||||
};
|
||||
|
||||
|
||||
@@ -7,13 +7,14 @@ package edu.wpi.first.cscore;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
import edu.wpi.first.util.PixelFormat;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class VideoModeTest {
|
||||
@Test
|
||||
void equalityTest() {
|
||||
VideoMode a = new VideoMode(VideoMode.PixelFormat.kMJPEG, 1920, 1080, 30);
|
||||
VideoMode b = new VideoMode(VideoMode.PixelFormat.kMJPEG, 1920, 1080, 30);
|
||||
VideoMode a = new VideoMode(PixelFormat.kMJPEG, 1920, 1080, 30);
|
||||
VideoMode b = new VideoMode(PixelFormat.kMJPEG, 1920, 1080, 30);
|
||||
|
||||
assertEquals(a, b);
|
||||
assertNotEquals(a, null);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* Autogenerated file! Do not manually edit this file. This version is regenerated
|
||||
* any time the publish task is run, or when this file is deleted.
|
||||
*/
|
||||
|
||||
@@ -1,72 +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.
|
||||
|
||||
#include "DataLogThread.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
DataLogThread::~DataLogThread() {
|
||||
if (m_thread.joinable()) {
|
||||
m_active = false;
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void DataLogThread::ReadMain() {
|
||||
for (auto record : m_reader) {
|
||||
if (!m_active) {
|
||||
break;
|
||||
}
|
||||
++m_numRecords;
|
||||
if (record.IsStart()) {
|
||||
wpi::log::StartRecordData data;
|
||||
if (record.GetStartData(&data)) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (m_entries.find(data.entry) != m_entries.end()) {
|
||||
fmt::print("...DUPLICATE entry ID, overriding\n");
|
||||
}
|
||||
m_entries[data.entry] = data;
|
||||
m_entryNames.emplace(data.name, data);
|
||||
sigEntryAdded(data);
|
||||
} else {
|
||||
fmt::print("Start(INVALID)\n");
|
||||
}
|
||||
} else if (record.IsFinish()) {
|
||||
int entry;
|
||||
if (record.GetFinishEntry(&entry)) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto it = m_entries.find(entry);
|
||||
if (it == m_entries.end()) {
|
||||
fmt::print("...ID not found\n");
|
||||
} else {
|
||||
m_entries.erase(it);
|
||||
}
|
||||
} else {
|
||||
fmt::print("Finish(INVALID)\n");
|
||||
}
|
||||
} else if (record.IsSetMetadata()) {
|
||||
wpi::log::MetadataRecordData data;
|
||||
if (record.GetSetMetadataData(&data)) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto it = m_entries.find(data.entry);
|
||||
if (it == m_entries.end()) {
|
||||
fmt::print("...ID not found\n");
|
||||
} else {
|
||||
it->second.metadata = data.metadata;
|
||||
auto nameIt = m_entryNames.find(it->second.name);
|
||||
if (nameIt != m_entryNames.end()) {
|
||||
nameIt->second.metadata = data.metadata;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt::print("SetMetadata(INVALID)\n");
|
||||
}
|
||||
} else if (record.IsControl()) {
|
||||
fmt::print("Unrecognized control record\n");
|
||||
}
|
||||
}
|
||||
|
||||
sigDone();
|
||||
m_done = true;
|
||||
}
|
||||
@@ -1,71 +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 <atomic>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include <wpi/DataLogReader.h>
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/Signal.h>
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
class DataLogThread {
|
||||
public:
|
||||
explicit DataLogThread(wpi::log::DataLogReader reader)
|
||||
: m_reader{std::move(reader)}, m_thread{[=, this] { ReadMain(); }} {}
|
||||
~DataLogThread();
|
||||
|
||||
bool IsDone() const { return m_done; }
|
||||
std::string_view GetBufferIdentifier() const {
|
||||
return m_reader.GetBufferIdentifier();
|
||||
}
|
||||
unsigned int GetNumRecords() const { return m_numRecords; }
|
||||
unsigned int GetNumEntries() const {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_entryNames.size();
|
||||
}
|
||||
|
||||
// Passes wpi::log::StartRecordData to func
|
||||
template <typename T>
|
||||
void ForEachEntryName(T&& func) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
for (auto&& kv : m_entryNames) {
|
||||
func(kv.second);
|
||||
}
|
||||
}
|
||||
|
||||
wpi::log::StartRecordData GetEntry(std::string_view name) const {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto it = m_entryNames.find(name);
|
||||
if (it == m_entryNames.end()) {
|
||||
return {};
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const wpi::log::DataLogReader& GetReader() const { return m_reader; }
|
||||
|
||||
// note: these are called on separate thread
|
||||
wpi::sig::Signal_mt<const wpi::log::StartRecordData&> sigEntryAdded;
|
||||
wpi::sig::Signal_mt<> sigDone;
|
||||
|
||||
private:
|
||||
void ReadMain();
|
||||
|
||||
wpi::log::DataLogReader m_reader;
|
||||
mutable wpi::mutex m_mutex;
|
||||
std::atomic_bool m_active{true};
|
||||
std::atomic_bool m_done{false};
|
||||
std::atomic<unsigned int> m_numRecords{0};
|
||||
std::map<std::string, wpi::log::StartRecordData, std::less<>> m_entryNames;
|
||||
wpi::DenseMap<int, wpi::log::StartRecordData> m_entries;
|
||||
std::thread m_thread;
|
||||
};
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
Downloader::Downloader(glass::Storage& storage)
|
||||
: m_serverTeam{storage.GetString("serverTeam")},
|
||||
m_remoteDir{storage.GetString("remoteDir", "/home/lvuser")},
|
||||
m_remoteDir{storage.GetString("remoteDir", "/home/lvuser/logs")},
|
||||
m_username{storage.GetString("username", "lvuser")},
|
||||
m_localDir{storage.GetString("localDir")},
|
||||
m_deleteAfter{storage.GetBool("deleteAfter", true)},
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <fmt/chrono.h>
|
||||
#include <fmt/format.h>
|
||||
#include <glass/Storage.h>
|
||||
#include <glass/support/DataLogReaderThread.h>
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <imgui_stdlib.h>
|
||||
@@ -32,11 +33,10 @@
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#include "App.h"
|
||||
#include "DataLogThread.h"
|
||||
|
||||
namespace {
|
||||
struct InputFile {
|
||||
explicit InputFile(std::unique_ptr<DataLogThread> datalog);
|
||||
explicit InputFile(std::unique_ptr<glass::DataLogReaderThread> datalog);
|
||||
|
||||
InputFile(std::string_view filename, std::string_view status)
|
||||
: filename{filename},
|
||||
@@ -47,7 +47,7 @@ struct InputFile {
|
||||
|
||||
std::string filename;
|
||||
std::string stem;
|
||||
std::unique_ptr<DataLogThread> datalog;
|
||||
std::unique_ptr<glass::DataLogReaderThread> datalog;
|
||||
std::string status;
|
||||
bool highlight = false;
|
||||
};
|
||||
@@ -135,7 +135,7 @@ static void RebuildEntryTree() {
|
||||
}
|
||||
}
|
||||
|
||||
InputFile::InputFile(std::unique_ptr<DataLogThread> datalog_)
|
||||
InputFile::InputFile(std::unique_ptr<glass::DataLogReaderThread> datalog_)
|
||||
: filename{datalog_->GetBufferIdentifier()},
|
||||
stem{fs::path{filename}.stem().string()},
|
||||
datalog{std::move(datalog_)} {
|
||||
@@ -192,7 +192,7 @@ static std::unique_ptr<InputFile> LoadDataLog(std::string_view filename) {
|
||||
}
|
||||
|
||||
return std::make_unique<InputFile>(
|
||||
std::make_unique<DataLogThread>(std::move(reader)));
|
||||
std::make_unique<glass::DataLogReaderThread>(std::move(reader)));
|
||||
}
|
||||
|
||||
void DisplayInputFiles() {
|
||||
@@ -284,9 +284,10 @@ static bool EmitEntry(const std::string& name, Entry& entry) {
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
for (auto inputFile : entry.inputFiles) {
|
||||
ImGui::Text(
|
||||
"%s: %s", inputFile->stem.c_str(),
|
||||
std::string{inputFile->datalog->GetEntry(entry.name).type}.c_str());
|
||||
if (auto info = inputFile->datalog->GetEntry(entry.name)) {
|
||||
ImGui::Text("%s: %s", inputFile->stem.c_str(),
|
||||
std::string{info->type}.c_str());
|
||||
}
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
@@ -300,10 +301,10 @@ static bool EmitEntry(const std::string& name, Entry& entry) {
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
for (auto inputFile : entry.inputFiles) {
|
||||
ImGui::Text(
|
||||
"%s: %s", inputFile->stem.c_str(),
|
||||
std::string{inputFile->datalog->GetEntry(entry.name).metadata}
|
||||
.c_str());
|
||||
if (auto info = inputFile->datalog->GetEntry(entry.name)) {
|
||||
ImGui::Text("%s: %s", inputFile->stem.c_str(),
|
||||
std::string{info->metadata}.c_str());
|
||||
}
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
@@ -208,21 +208,21 @@ configurations {
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
sharedCvConfigs = [:]
|
||||
staticCvConfigs = [:]
|
||||
useJava = true
|
||||
useCpp = false
|
||||
skipDev = true
|
||||
useDocumentation = true
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/shared/opencv.gradle"
|
||||
|
||||
task generateJavaDocs(type: Javadoc) {
|
||||
classpath += project(":wpimath").sourceSets.main.compileClasspath
|
||||
classpath += project(":wpilibj").sourceSets.main.compileClasspath
|
||||
options.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
|
||||
options.links("https://docs.opencv.org/4.x/javadoc/")
|
||||
options.addStringOption("tag", "pre:a:Pre-Condition")
|
||||
options.addBooleanOption("Xdoclint/package:-edu.wpi.first.math.proto," +
|
||||
"-edu.wpi.first.math.controller.proto," +
|
||||
"-edu.wpi.first.math.controller.struct," +
|
||||
"-edu.wpi.first.math.geometry.proto," +
|
||||
"-edu.wpi.first.math.geometry.struct," +
|
||||
"-edu.wpi.first.math.kinematics.proto," +
|
||||
"-edu.wpi.first.math.kinematics.struct," +
|
||||
"-edu.wpi.first.math.system.plant.proto," +
|
||||
"-edu.wpi.first.math.system.plant.struct," +
|
||||
"-edu.wpi.first.math.trajectory.proto", true)
|
||||
options.addBooleanOption("Xdoclint:html,missing,reference,syntax", true)
|
||||
options.addBooleanOption('html5', true)
|
||||
options.linkSource(true)
|
||||
|
||||
@@ -15,12 +15,13 @@ public enum Fields {
|
||||
k2021GalacticSearchB("2021-galacticsearchb.json"),
|
||||
k2021Slalom("2021-slalompath.json"),
|
||||
k2022RapidReact("2022-rapidreact.json"),
|
||||
k2023ChargedUp("2023-chargedup.json");
|
||||
k2023ChargedUp("2023-chargedup.json"),
|
||||
k2024Crescendo("2024-crescendo.json");
|
||||
|
||||
public static final String kBaseResourceDir = "/edu/wpi/first/fields/";
|
||||
|
||||
/** Alias to the current game. */
|
||||
public static final Fields kDefaultField = k2023ChargedUp;
|
||||
public static final Fields kDefaultField = k2024Crescendo;
|
||||
|
||||
public final String m_resourceFile;
|
||||
|
||||
|
||||
@@ -15,10 +15,13 @@
|
||||
#include "fields/2021-slalom.h"
|
||||
#include "fields/2022-rapidreact.h"
|
||||
#include "fields/2023-chargedup.h"
|
||||
#include "fields/2024-crescendo.h"
|
||||
|
||||
using namespace fields;
|
||||
|
||||
static const Field kFields[] = {
|
||||
{"2024 Crescendo", GetResource_2024_crescendo_json,
|
||||
GetResource_2024_field_png},
|
||||
{"2023 Charged Up", GetResource_2023_chargedup_json,
|
||||
GetResource_2023_field_png},
|
||||
{"2022 Rapid React", GetResource_2022_rapidreact_json,
|
||||
|
||||
12
fieldImages/src/main/native/include/fields/2024-crescendo.h
Normal file
12
fieldImages/src/main/native/include/fields/2024-crescendo.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace fields {
|
||||
std::string_view GetResource_2024_crescendo_json();
|
||||
std::string_view GetResource_2024_field_png();
|
||||
} // namespace fields
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"game": "Crescendo",
|
||||
"field-image": "2024-field.png",
|
||||
"field-corners": {
|
||||
"top-left": [
|
||||
46,
|
||||
36
|
||||
],
|
||||
"bottom-right": [
|
||||
1088,
|
||||
544
|
||||
]
|
||||
},
|
||||
"field-size": [
|
||||
54.27083,
|
||||
26.2916
|
||||
],
|
||||
"field-unit": "foot"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* Autogenerated file! Do not manually edit this file. This version is regenerated
|
||||
* any time the publish task is run, or when this file is deleted.
|
||||
*/
|
||||
|
||||
@@ -178,8 +178,6 @@ static bool LoadStorageRootImpl(Context* ctx, const std::string& filename,
|
||||
|
||||
static bool LoadStorageImpl(Context* ctx, std::string_view dir,
|
||||
std::string_view name) {
|
||||
WorkspaceResetImpl();
|
||||
|
||||
bool rv = true;
|
||||
for (auto&& root : ctx->storageRoots) {
|
||||
std::string filename;
|
||||
@@ -421,6 +419,7 @@ std::string glass::GetStorageDir() {
|
||||
bool glass::LoadStorage(std::string_view dir) {
|
||||
SaveStorage();
|
||||
SetStorageDir(dir);
|
||||
WorkspaceResetImpl();
|
||||
LoadWindowStorageImpl((fs::path{gContext->storageLoadDir} /
|
||||
fmt::format("{}-window.json", gContext->storageName))
|
||||
.string());
|
||||
|
||||
@@ -29,6 +29,16 @@ void glass::DisplayPIDController(PIDControllerModel* m) {
|
||||
callback(*v);
|
||||
}
|
||||
};
|
||||
// Workaround to allow for the input of inf, -inf, and nan
|
||||
auto createTuningParameterNoFilter =
|
||||
[flag](const char* name, double* v,
|
||||
std::function<void(double)> callback) {
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
|
||||
if (ImGui::InputScalar(name, ImGuiDataType_Double, v, NULL, NULL,
|
||||
"%.3f", flag)) {
|
||||
callback(*v);
|
||||
}
|
||||
};
|
||||
|
||||
if (auto p = m->GetPData()) {
|
||||
double value = p->GetValue();
|
||||
@@ -47,6 +57,11 @@ void glass::DisplayPIDController(PIDControllerModel* m) {
|
||||
createTuningParameter("Setpoint", &value,
|
||||
[=](auto v) { m->SetSetpoint(v); });
|
||||
}
|
||||
if (auto s = m->GetIZoneData()) {
|
||||
double value = s->GetValue();
|
||||
createTuningParameterNoFilter("IZone", &value,
|
||||
[=](auto v) { m->SetIZone(v); });
|
||||
}
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(96, 96, 96, 255));
|
||||
ImGui::Text("Unknown PID Controller");
|
||||
|
||||
69
glass/src/lib/native/cpp/other/ProfiledPIDController.cpp
Normal file
69
glass/src/lib/native/cpp/other/ProfiledPIDController.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// 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/ProfiledPIDController.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/DataSource.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
void glass::DisplayProfiledPIDController(ProfiledPIDControllerModel* m) {
|
||||
if (auto name = m->GetName()) {
|
||||
ImGui::Text("%s", name);
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
if (m->Exists()) {
|
||||
auto flag = m->IsReadOnly() ? ImGuiInputTextFlags_ReadOnly
|
||||
: ImGuiInputTextFlags_None;
|
||||
auto createTuningParameter = [flag](const char* name, double* v,
|
||||
std::function<void(double)> callback) {
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
|
||||
if (ImGui::InputDouble(name, v, 0.0, 0.0, "%.3f", flag)) {
|
||||
callback(*v);
|
||||
}
|
||||
};
|
||||
// Workaround to allow for the input of inf, -inf, and nan
|
||||
auto createTuningParameterNoFilter =
|
||||
[flag](const char* name, double* v,
|
||||
std::function<void(double)> callback) {
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
|
||||
if (ImGui::InputScalar(name, ImGuiDataType_Double, v, NULL, NULL,
|
||||
"%.3f", flag)) {
|
||||
callback(*v);
|
||||
}
|
||||
};
|
||||
|
||||
if (auto p = m->GetPData()) {
|
||||
double value = p->GetValue();
|
||||
createTuningParameter("P", &value, [=](auto v) { m->SetP(v); });
|
||||
}
|
||||
if (auto i = m->GetIData()) {
|
||||
double value = i->GetValue();
|
||||
createTuningParameter("I", &value, [=](auto v) { m->SetI(v); });
|
||||
}
|
||||
if (auto d = m->GetDData()) {
|
||||
double value = d->GetValue();
|
||||
createTuningParameter("D", &value, [=](auto v) { m->SetD(v); });
|
||||
}
|
||||
if (auto s = m->GetGoalData()) {
|
||||
double value = s->GetValue();
|
||||
createTuningParameter("Goal", &value, [=](auto v) { m->SetGoal(v); });
|
||||
}
|
||||
if (auto s = m->GetIZoneData()) {
|
||||
double value = s->GetValue();
|
||||
createTuningParameterNoFilter("IZone", &value,
|
||||
[=](auto v) { m->SetIZone(v); });
|
||||
}
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(96, 96, 96, 255));
|
||||
ImGui::Text("Unknown PID Controller");
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
}
|
||||
124
glass/src/lib/native/cpp/support/DataLogReaderThread.cpp
Normal file
124
glass/src/lib/native/cpp/support/DataLogReaderThread.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
// 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/support/DataLogReaderThread.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
using namespace glass;
|
||||
|
||||
DataLogReaderThread::~DataLogReaderThread() {
|
||||
if (m_thread.joinable()) {
|
||||
m_active = false;
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void DataLogReaderThread::ReadMain() {
|
||||
wpi::SmallDenseMap<
|
||||
int, std::pair<DataLogReaderEntry*, std::span<const uint8_t>>, 8>
|
||||
schemaEntries;
|
||||
|
||||
for (auto recordIt = m_reader.begin(), recordEnd = m_reader.end();
|
||||
recordIt != recordEnd; ++recordIt) {
|
||||
auto& record = *recordIt;
|
||||
if (!m_active) {
|
||||
break;
|
||||
}
|
||||
++m_numRecords;
|
||||
if (record.IsStart()) {
|
||||
DataLogReaderEntry data;
|
||||
if (record.GetStartData(&data)) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto& entryPtr = m_entriesById[data.entry];
|
||||
if (entryPtr) {
|
||||
fmt::print("...DUPLICATE entry ID, overriding\n");
|
||||
}
|
||||
auto [it, isNew] = m_entriesByName.emplace(data.name, data);
|
||||
if (isNew) {
|
||||
it->second.ranges.emplace_back(recordIt, recordEnd);
|
||||
}
|
||||
entryPtr = &it->second;
|
||||
if (data.type == "structschema" ||
|
||||
data.type == "proto:FileDescriptorProto") {
|
||||
schemaEntries.try_emplace(data.entry, entryPtr,
|
||||
std::span<const uint8_t>{});
|
||||
}
|
||||
sigEntryAdded(data);
|
||||
} else {
|
||||
fmt::print("Start(INVALID)\n");
|
||||
}
|
||||
} else if (record.IsFinish()) {
|
||||
int entry;
|
||||
if (record.GetFinishEntry(&entry)) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto it = m_entriesById.find(entry);
|
||||
if (it == m_entriesById.end()) {
|
||||
fmt::print("...ID not found\n");
|
||||
} else {
|
||||
it->second->ranges.back().m_end = recordIt;
|
||||
m_entriesById.erase(it);
|
||||
}
|
||||
} else {
|
||||
fmt::print("Finish(INVALID)\n");
|
||||
}
|
||||
} else if (record.IsSetMetadata()) {
|
||||
wpi::log::MetadataRecordData data;
|
||||
if (record.GetSetMetadataData(&data)) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto it = m_entriesById.find(data.entry);
|
||||
if (it == m_entriesById.end()) {
|
||||
fmt::print("...ID not found\n");
|
||||
} else {
|
||||
it->second->metadata = data.metadata;
|
||||
}
|
||||
} else {
|
||||
fmt::print("SetMetadata(INVALID)\n");
|
||||
}
|
||||
} else if (record.IsControl()) {
|
||||
fmt::print("Unrecognized control record\n");
|
||||
} else {
|
||||
auto it = schemaEntries.find(record.GetEntry());
|
||||
if (it != schemaEntries.end()) {
|
||||
it->second.second = record.GetRaw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build schema databases
|
||||
for (auto&& schemaPair : schemaEntries) {
|
||||
auto name = schemaPair.second.first->name;
|
||||
auto data = schemaPair.second.second;
|
||||
if (data.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (wpi::starts_with(name, "NT:")) {
|
||||
name = wpi::drop_front(name, 3);
|
||||
}
|
||||
if (wpi::starts_with(name, "/.schema/struct:")) {
|
||||
auto typeStr = wpi::drop_front(name, 16);
|
||||
std::string_view schema{reinterpret_cast<const char*>(data.data()),
|
||||
data.size()};
|
||||
std::string err;
|
||||
auto desc = m_structDb.Add(typeStr, schema, &err);
|
||||
if (!desc) {
|
||||
fmt::print("could not decode struct '{}' schema '{}': {}\n", name,
|
||||
schema, err);
|
||||
}
|
||||
} else if (wpi::starts_with(name, "/.schema/proto:")) {
|
||||
// protobuf descriptor handling
|
||||
auto filename = wpi::drop_front(name, 15);
|
||||
if (!m_protoDb.Add(filename, data)) {
|
||||
fmt::print("could not decode protobuf '{}' filename '{}'\n", name,
|
||||
filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sigDone();
|
||||
m_done = true;
|
||||
}
|
||||
@@ -16,11 +16,13 @@ class PIDControllerModel : public Model {
|
||||
virtual DataSource* GetIData() = 0;
|
||||
virtual DataSource* GetDData() = 0;
|
||||
virtual DataSource* GetSetpointData() = 0;
|
||||
virtual DataSource* GetIZoneData() = 0;
|
||||
|
||||
virtual void SetP(double value) = 0;
|
||||
virtual void SetI(double value) = 0;
|
||||
virtual void SetD(double value) = 0;
|
||||
virtual void SetSetpoint(double value) = 0;
|
||||
virtual void SetIZone(double value) = 0;
|
||||
};
|
||||
void DisplayPIDController(PIDControllerModel* m);
|
||||
} // namespace glass
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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 "glass/Model.h"
|
||||
|
||||
namespace glass {
|
||||
class DataSource;
|
||||
class ProfiledPIDControllerModel : public Model {
|
||||
public:
|
||||
virtual const char* GetName() const = 0;
|
||||
|
||||
virtual DataSource* GetPData() = 0;
|
||||
virtual DataSource* GetIData() = 0;
|
||||
virtual DataSource* GetDData() = 0;
|
||||
virtual DataSource* GetGoalData() = 0;
|
||||
virtual DataSource* GetIZoneData() = 0;
|
||||
|
||||
virtual void SetP(double value) = 0;
|
||||
virtual void SetI(double value) = 0;
|
||||
virtual void SetD(double value) = 0;
|
||||
virtual void SetGoal(double value) = 0;
|
||||
virtual void SetIZone(double value) = 0;
|
||||
};
|
||||
void DisplayProfiledPIDController(ProfiledPIDControllerModel* m);
|
||||
} // namespace glass
|
||||
101
glass/src/lib/native/include/glass/support/DataLogReaderThread.h
Normal file
101
glass/src/lib/native/include/glass/support/DataLogReaderThread.h
Normal file
@@ -0,0 +1,101 @@
|
||||
// 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 <atomic>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/DataLogReader.h>
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/Signal.h>
|
||||
#include <wpi/mutex.h>
|
||||
#include <wpi/protobuf/ProtobufMessageDatabase.h>
|
||||
#include <wpi/struct/DynamicStruct.h>
|
||||
|
||||
namespace glass {
|
||||
|
||||
class DataLogReaderRange {
|
||||
public:
|
||||
DataLogReaderRange(wpi::log::DataLogReader::iterator begin,
|
||||
wpi::log::DataLogReader::iterator end)
|
||||
: m_begin{begin}, m_end{end} {}
|
||||
|
||||
wpi::log::DataLogReader::iterator begin() const { return m_begin; }
|
||||
wpi::log::DataLogReader::iterator end() const { return m_end; }
|
||||
|
||||
wpi::log::DataLogReader::iterator m_begin;
|
||||
wpi::log::DataLogReader::iterator m_end;
|
||||
};
|
||||
|
||||
class DataLogReaderEntry : public wpi::log::StartRecordData {
|
||||
public:
|
||||
std::vector<DataLogReaderRange> ranges; // ranges where this entry is valid
|
||||
};
|
||||
|
||||
class DataLogReaderThread {
|
||||
public:
|
||||
explicit DataLogReaderThread(wpi::log::DataLogReader reader)
|
||||
: m_reader{std::move(reader)}, m_thread{[this] { ReadMain(); }} {}
|
||||
~DataLogReaderThread();
|
||||
|
||||
bool IsDone() const { return m_done; }
|
||||
std::string_view GetBufferIdentifier() const {
|
||||
return m_reader.GetBufferIdentifier();
|
||||
}
|
||||
unsigned int GetNumRecords() const { return m_numRecords; }
|
||||
unsigned int GetNumEntries() const {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_entriesByName.size();
|
||||
}
|
||||
|
||||
// Passes Entry& to func
|
||||
template <typename T>
|
||||
void ForEachEntryName(T&& func) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
for (auto&& kv : m_entriesByName) {
|
||||
func(kv.second);
|
||||
}
|
||||
}
|
||||
|
||||
const DataLogReaderEntry* GetEntry(std::string_view name) const {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto it = m_entriesByName.find(name);
|
||||
if (it == m_entriesByName.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
wpi::StructDescriptorDatabase& GetStructDatabase() { return m_structDb; }
|
||||
wpi::ProtobufMessageDatabase& GetProtobufDatabase() { return m_protoDb; }
|
||||
|
||||
const wpi::log::DataLogReader& GetReader() const { return m_reader; }
|
||||
|
||||
// note: these are called on separate thread
|
||||
wpi::sig::Signal_mt<const DataLogReaderEntry&> sigEntryAdded;
|
||||
wpi::sig::Signal_mt<> sigDone;
|
||||
|
||||
private:
|
||||
void ReadMain();
|
||||
|
||||
wpi::log::DataLogReader m_reader;
|
||||
mutable wpi::mutex m_mutex;
|
||||
std::atomic_bool m_active{true};
|
||||
std::atomic_bool m_done{false};
|
||||
std::atomic<unsigned int> m_numRecords{0};
|
||||
std::map<std::string, DataLogReaderEntry, std::less<>> m_entriesByName;
|
||||
wpi::DenseMap<int, DataLogReaderEntry*> m_entriesById;
|
||||
wpi::StructDescriptorDatabase m_structDb;
|
||||
wpi::ProtobufMessageDatabase m_protoDb;
|
||||
std::thread m_thread;
|
||||
};
|
||||
|
||||
} // namespace glass
|
||||
@@ -23,10 +23,12 @@ NTPIDControllerModel::NTPIDControllerModel(nt::NetworkTableInstance inst,
|
||||
m_d{inst.GetDoubleTopic(fmt::format("{}/d", path)).GetEntry(0)},
|
||||
m_setpoint{
|
||||
inst.GetDoubleTopic(fmt::format("{}/setpoint", path)).GetEntry(0)},
|
||||
m_iZone{inst.GetDoubleTopic(fmt::format("{}/izone", path)).GetEntry(0)},
|
||||
m_pData{fmt::format("NTPIDCtrlP:{}", path)},
|
||||
m_iData{fmt::format("NTPIDCtrlI:{}", path)},
|
||||
m_dData{fmt::format("NTPIDCtrlD:{}", path)},
|
||||
m_setpointData{fmt::format("NTPIDCtrlStpt:{}", path)},
|
||||
m_iZoneData{fmt::format("NTPIDCtrlIZone:{}", path)},
|
||||
m_nameValue{wpi::rsplit(path, '/').second} {}
|
||||
|
||||
void NTPIDControllerModel::SetP(double value) {
|
||||
@@ -44,6 +46,9 @@ void NTPIDControllerModel::SetD(double value) {
|
||||
void NTPIDControllerModel::SetSetpoint(double value) {
|
||||
m_setpoint.Set(value);
|
||||
}
|
||||
void NTPIDControllerModel::SetIZone(double value) {
|
||||
m_iZone.Set(value);
|
||||
}
|
||||
|
||||
void NTPIDControllerModel::Update() {
|
||||
for (auto&& v : m_name.ReadQueue()) {
|
||||
@@ -61,6 +66,9 @@ void NTPIDControllerModel::Update() {
|
||||
for (auto&& v : m_setpoint.ReadQueue()) {
|
||||
m_setpointData.SetValue(v.value, v.time);
|
||||
}
|
||||
for (auto&& v : m_iZone.ReadQueue()) {
|
||||
m_iZoneData.SetValue(v.value, v.time);
|
||||
}
|
||||
for (auto&& v : m_controllable.ReadQueue()) {
|
||||
m_controllableValue = v.value;
|
||||
}
|
||||
|
||||
80
glass/src/libnt/native/cpp/NTProfiledPIDController.cpp
Normal file
80
glass/src/libnt/native/cpp/NTProfiledPIDController.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
// 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/NTProfiledPIDController.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
using namespace glass;
|
||||
|
||||
NTProfiledPIDControllerModel::NTProfiledPIDControllerModel(
|
||||
std::string_view path)
|
||||
: NTProfiledPIDControllerModel(nt::NetworkTableInstance::GetDefault(),
|
||||
path) {}
|
||||
|
||||
NTProfiledPIDControllerModel::NTProfiledPIDControllerModel(
|
||||
nt::NetworkTableInstance inst, std::string_view path)
|
||||
: m_inst{inst},
|
||||
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
|
||||
m_controllable{inst.GetBooleanTopic(fmt::format("{}/.controllable", path))
|
||||
.Subscribe(false)},
|
||||
m_p{inst.GetDoubleTopic(fmt::format("{}/p", path)).GetEntry(0)},
|
||||
m_i{inst.GetDoubleTopic(fmt::format("{}/i", path)).GetEntry(0)},
|
||||
m_d{inst.GetDoubleTopic(fmt::format("{}/d", path)).GetEntry(0)},
|
||||
m_goal{inst.GetDoubleTopic(fmt::format("{}/goal", path)).GetEntry(0)},
|
||||
m_iZone{inst.GetDoubleTopic(fmt::format("{}/izone", path)).GetEntry(0)},
|
||||
m_pData{fmt::format("NTPIDCtrlP:{}", path)},
|
||||
m_iData{fmt::format("NTPIDCtrlI:{}", path)},
|
||||
m_dData{fmt::format("NTPIDCtrlD:{}", path)},
|
||||
m_goalData{fmt::format("NTPIDCtrlGoal:{}", path)},
|
||||
m_iZoneData{fmt::format("NTPIDCtrlIZone:{}", path)},
|
||||
m_nameValue{wpi::rsplit(path, '/').second} {}
|
||||
|
||||
void NTProfiledPIDControllerModel::SetP(double value) {
|
||||
m_p.Set(value);
|
||||
}
|
||||
|
||||
void NTProfiledPIDControllerModel::SetI(double value) {
|
||||
m_i.Set(value);
|
||||
}
|
||||
|
||||
void NTProfiledPIDControllerModel::SetD(double value) {
|
||||
m_d.Set(value);
|
||||
}
|
||||
|
||||
void NTProfiledPIDControllerModel::SetGoal(double value) {
|
||||
m_goal.Set(value);
|
||||
}
|
||||
void NTProfiledPIDControllerModel::SetIZone(double value) {
|
||||
m_iZone.Set(value);
|
||||
}
|
||||
|
||||
void NTProfiledPIDControllerModel::Update() {
|
||||
for (auto&& v : m_name.ReadQueue()) {
|
||||
m_nameValue = std::move(v.value);
|
||||
}
|
||||
for (auto&& v : m_p.ReadQueue()) {
|
||||
m_pData.SetValue(v.value, v.time);
|
||||
}
|
||||
for (auto&& v : m_i.ReadQueue()) {
|
||||
m_iData.SetValue(v.value, v.time);
|
||||
}
|
||||
for (auto&& v : m_d.ReadQueue()) {
|
||||
m_dData.SetValue(v.value, v.time);
|
||||
}
|
||||
for (auto&& v : m_goal.ReadQueue()) {
|
||||
m_goalData.SetValue(v.value, v.time);
|
||||
}
|
||||
for (auto&& v : m_iZone.ReadQueue()) {
|
||||
m_iZoneData.SetValue(v.value, v.time);
|
||||
}
|
||||
for (auto&& v : m_controllable.ReadQueue()) {
|
||||
m_controllableValue = v.value;
|
||||
}
|
||||
}
|
||||
|
||||
bool NTProfiledPIDControllerModel::Exists() {
|
||||
return m_goal.Exists();
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "glass/networktables/NTStringChooser.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/json.h>
|
||||
|
||||
using namespace glass;
|
||||
|
||||
@@ -16,17 +17,17 @@ NTStringChooserModel::NTStringChooserModel(nt::NetworkTableInstance inst,
|
||||
: m_inst{inst},
|
||||
m_default{
|
||||
m_inst.GetStringTopic(fmt::format("{}/default", path)).Subscribe("")},
|
||||
m_selected{
|
||||
m_inst.GetStringTopic(fmt::format("{}/selected", path)).GetEntry("")},
|
||||
m_selected{m_inst.GetStringTopic(fmt::format("{}/selected", path))
|
||||
.Subscribe("")},
|
||||
m_selectedPub{m_inst.GetStringTopic(fmt::format("{}/selected", path))
|
||||
.PublishEx("string", {{"retained", true}})},
|
||||
m_active{
|
||||
m_inst.GetStringTopic(fmt::format("{}/active", path)).Subscribe("")},
|
||||
m_options{m_inst.GetStringArrayTopic(fmt::format("{}/options", path))
|
||||
.Subscribe({})} {
|
||||
m_selected.GetTopic().SetRetained(true);
|
||||
}
|
||||
.Subscribe({})} {}
|
||||
|
||||
void NTStringChooserModel::SetSelected(std::string_view val) {
|
||||
m_selected.Set(val);
|
||||
m_selectedPub.Set(val);
|
||||
}
|
||||
|
||||
void NTStringChooserModel::Update() {
|
||||
|
||||
@@ -879,7 +879,10 @@ void NetworkTablesModel::Update() {
|
||||
} else if (desc->IsValid()) {
|
||||
// loop over all entries with this type and update
|
||||
for (auto&& entryPair : m_entries) {
|
||||
auto ts = entryPair.second->info.type_str;
|
||||
if (!entryPair.second) {
|
||||
continue;
|
||||
}
|
||||
std::string_view ts = entryPair.second->info.type_str;
|
||||
if (!wpi::starts_with(ts, "struct:")) {
|
||||
continue;
|
||||
}
|
||||
@@ -901,7 +904,10 @@ void NetworkTablesModel::Update() {
|
||||
} else {
|
||||
// loop over all protobuf entries and update (conservatively)
|
||||
for (auto&& entryPair : m_entries) {
|
||||
auto& ts = entryPair.second->info.type_str;
|
||||
if (!entryPair.second) {
|
||||
continue;
|
||||
}
|
||||
std::string_view ts = entryPair.second->info.type_str;
|
||||
if (wpi::starts_with(ts, "proto:")) {
|
||||
entryPair.second->UpdateFromValue(*this);
|
||||
}
|
||||
|
||||
@@ -83,6 +83,18 @@ void NetworkTablesProvider::DisplayMenu() {
|
||||
// FIXME: enabled?
|
||||
// data is the last item, so is guaranteed to be null-terminated
|
||||
ImGui::MenuItem(path.back().data(), nullptr, &visible, true);
|
||||
// Add type label to smartdashboard sendables
|
||||
if (wpi::starts_with(entry->name, "/SmartDashboard/")) {
|
||||
auto typeEntry = m_typeCache.FindValue(entry->name);
|
||||
if (typeEntry) {
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(96, 96, 96, 255));
|
||||
ImGui::Text("%s", typeEntry->stringVal.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(10.0f, 0.0f));
|
||||
}
|
||||
}
|
||||
if (!wasVisible && visible) {
|
||||
Show(entry.get(), entry->window);
|
||||
} else if (wasVisible && !visible && entry->window) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "glass/networktables/NTMechanism2D.h"
|
||||
#include "glass/networktables/NTMotorController.h"
|
||||
#include "glass/networktables/NTPIDController.h"
|
||||
#include "glass/networktables/NTProfiledPIDController.h"
|
||||
#include "glass/networktables/NTStringChooser.h"
|
||||
#include "glass/networktables/NTSubsystem.h"
|
||||
#include "glass/networktables/NetworkTablesProvider.h"
|
||||
@@ -141,6 +142,18 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
|
||||
DisplayPIDController(static_cast<NTPIDControllerModel*>(model));
|
||||
});
|
||||
});
|
||||
provider.Register(
|
||||
NTProfiledPIDControllerModel::kType,
|
||||
[](nt::NetworkTableInstance inst, const char* path) {
|
||||
return std::make_unique<NTProfiledPIDControllerModel>(inst, path);
|
||||
},
|
||||
[](Window* win, Model* model, const char* path) {
|
||||
win->SetFlags(ImGuiWindowFlags_AlwaysAutoResize);
|
||||
return MakeFunctionView([=] {
|
||||
DisplayProfiledPIDController(
|
||||
static_cast<NTProfiledPIDControllerModel*>(model));
|
||||
});
|
||||
});
|
||||
provider.Register(
|
||||
NTMotorControllerModel::kType,
|
||||
[](nt::NetworkTableInstance inst, const char* path) {
|
||||
|
||||
@@ -29,11 +29,13 @@ class NTPIDControllerModel : public PIDControllerModel {
|
||||
DataSource* GetIData() override { return &m_iData; }
|
||||
DataSource* GetDData() override { return &m_dData; }
|
||||
DataSource* GetSetpointData() override { return &m_setpointData; }
|
||||
DataSource* GetIZoneData() override { return &m_iZoneData; }
|
||||
|
||||
void SetP(double value) override;
|
||||
void SetI(double value) override;
|
||||
void SetD(double value) override;
|
||||
void SetSetpoint(double value) override;
|
||||
void SetIZone(double value) override;
|
||||
|
||||
void Update() override;
|
||||
bool Exists() override;
|
||||
@@ -47,11 +49,13 @@ class NTPIDControllerModel : public PIDControllerModel {
|
||||
nt::DoubleEntry m_i;
|
||||
nt::DoubleEntry m_d;
|
||||
nt::DoubleEntry m_setpoint;
|
||||
nt::DoubleEntry m_iZone;
|
||||
|
||||
DataSource m_pData;
|
||||
DataSource m_iData;
|
||||
DataSource m_dData;
|
||||
DataSource m_setpointData;
|
||||
DataSource m_iZoneData;
|
||||
|
||||
std::string m_nameValue;
|
||||
bool m_controllableValue = false;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// 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 <networktables/BooleanTopic.h>
|
||||
#include <networktables/DoubleTopic.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <networktables/StringTopic.h>
|
||||
|
||||
#include "glass/DataSource.h"
|
||||
#include "glass/other/ProfiledPIDController.h"
|
||||
|
||||
namespace glass {
|
||||
class NTProfiledPIDControllerModel : public ProfiledPIDControllerModel {
|
||||
public:
|
||||
static constexpr const char* kType = "ProfiledPIDController";
|
||||
|
||||
explicit NTProfiledPIDControllerModel(std::string_view path);
|
||||
NTProfiledPIDControllerModel(nt::NetworkTableInstance inst,
|
||||
std::string_view path);
|
||||
|
||||
const char* GetName() const override { return m_nameValue.c_str(); }
|
||||
|
||||
DataSource* GetPData() override { return &m_pData; }
|
||||
DataSource* GetIData() override { return &m_iData; }
|
||||
DataSource* GetDData() override { return &m_dData; }
|
||||
DataSource* GetGoalData() override { return &m_goalData; }
|
||||
DataSource* GetIZoneData() override { return &m_iZoneData; }
|
||||
|
||||
void SetP(double value) override;
|
||||
void SetI(double value) override;
|
||||
void SetD(double value) override;
|
||||
void SetGoal(double value) override;
|
||||
void SetIZone(double value) override;
|
||||
|
||||
void Update() override;
|
||||
bool Exists() override;
|
||||
bool IsReadOnly() override { return !m_controllableValue; }
|
||||
|
||||
private:
|
||||
nt::NetworkTableInstance m_inst;
|
||||
nt::StringSubscriber m_name;
|
||||
nt::BooleanSubscriber m_controllable;
|
||||
nt::DoubleEntry m_p;
|
||||
nt::DoubleEntry m_i;
|
||||
nt::DoubleEntry m_d;
|
||||
nt::DoubleEntry m_goal;
|
||||
nt::DoubleEntry m_iZone;
|
||||
|
||||
DataSource m_pData;
|
||||
DataSource m_iData;
|
||||
DataSource m_dData;
|
||||
DataSource m_goalData;
|
||||
DataSource m_iZoneData;
|
||||
|
||||
std::string m_nameValue;
|
||||
bool m_controllableValue = false;
|
||||
};
|
||||
} // namespace glass
|
||||
@@ -39,7 +39,8 @@ class NTStringChooserModel : public StringChooserModel {
|
||||
private:
|
||||
nt::NetworkTableInstance m_inst;
|
||||
nt::StringSubscriber m_default;
|
||||
nt::StringEntry m_selected;
|
||||
nt::StringSubscriber m_selected;
|
||||
nt::StringPublisher m_selectedPub;
|
||||
nt::StringSubscriber m_active;
|
||||
nt::StringArraySubscriber m_options;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ ext {
|
||||
|
||||
apply from: "${rootDir}/shared/jni/setupBuild.gradle"
|
||||
|
||||
sourceSets.main.java.srcDir "src/generated/main/java/edu/wpi/first/hal/"
|
||||
sourceSets.main.java.srcDir "${projectDir}/src/generated/main/java"
|
||||
|
||||
cppSourcesZip {
|
||||
from('src/main/native/athena') {
|
||||
|
||||
@@ -9,7 +9,7 @@ package edu.wpi.first.hal;
|
||||
/**
|
||||
* JNI wrapper for library <b>FRC_NetworkCommunication</b><br>.
|
||||
*/
|
||||
public class FRCNetComm {
|
||||
public final class FRCNetComm {
|
||||
/**
|
||||
* Resource type from UsageReporting.
|
||||
*/
|
||||
@@ -31,4 +31,7 @@ ${usage_reporting_types}
|
||||
|
||||
${usage_reporting_instances}
|
||||
}
|
||||
|
||||
/** Utility class. */
|
||||
private FRCNetComm() {}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ kADXL345_I2C = 2
|
||||
kCommand_Scheduler = 1
|
||||
kCommand2_Scheduler = 2
|
||||
kSmartDashboard_Instance = 1
|
||||
kSmartDashboard_LiveWindow = 2
|
||||
kKinematics_DifferentialDrive = 1
|
||||
kKinematics_MecanumDrive = 2
|
||||
kKinematics_SwerveDrive = 3
|
||||
|
||||
@@ -9,7 +9,7 @@ package edu.wpi.first.hal;
|
||||
/**
|
||||
* JNI wrapper for library <b>FRC_NetworkCommunication</b><br>.
|
||||
*/
|
||||
public class FRCNetComm {
|
||||
public final class FRCNetComm {
|
||||
/**
|
||||
* Resource type from UsageReporting.
|
||||
*/
|
||||
@@ -354,6 +354,8 @@ public class FRCNetComm {
|
||||
public static final int kCommand2_Scheduler = 2;
|
||||
/** kSmartDashboard_Instance = 1. */
|
||||
public static final int kSmartDashboard_Instance = 1;
|
||||
/** kSmartDashboard_LiveWindow = 2. */
|
||||
public static final int kSmartDashboard_LiveWindow = 2;
|
||||
/** kKinematics_DifferentialDrive = 1. */
|
||||
public static final int kKinematics_DifferentialDrive = 1;
|
||||
/** kKinematics_MecanumDrive = 2. */
|
||||
@@ -367,4 +369,7 @@ public class FRCNetComm {
|
||||
/** kOdometry_SwerveDrive = 3. */
|
||||
public static final int kOdometry_SwerveDrive = 3;
|
||||
}
|
||||
|
||||
/** Utility class. */
|
||||
private FRCNetComm() {}
|
||||
}
|
||||
|
||||
@@ -216,6 +216,7 @@ namespace HALUsageReporting {
|
||||
kCommand_Scheduler = 1,
|
||||
kCommand2_Scheduler = 2,
|
||||
kSmartDashboard_Instance = 1,
|
||||
kSmartDashboard_LiveWindow = 2,
|
||||
kKinematics_DifferentialDrive = 1,
|
||||
kKinematics_MecanumDrive = 2,
|
||||
kKinematics_SwerveDrive = 3,
|
||||
|
||||
@@ -59,4 +59,7 @@ public class AccelerometerJNI extends JNIWrapper {
|
||||
* @return the Z acceleration
|
||||
*/
|
||||
public static native double getAccelerometerZ();
|
||||
|
||||
/** Utility class. */
|
||||
private AccelerometerJNI() {}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ public class AccumulatorResult {
|
||||
/** The number of sample value was accumulated over. */
|
||||
public long count;
|
||||
|
||||
/** Constructs an AccumulatorResult. */
|
||||
public AccumulatorResult() {}
|
||||
|
||||
/**
|
||||
* Set the value and count.
|
||||
*
|
||||
|
||||
@@ -97,4 +97,7 @@ public class AddressableLEDJNI extends JNIWrapper {
|
||||
* @see "HAL_StopAddressableLEDOutput"
|
||||
*/
|
||||
public static native void stop(int handle);
|
||||
|
||||
/** Utility class. */
|
||||
private AddressableLEDJNI() {}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,20 @@
|
||||
|
||||
package edu.wpi.first.hal;
|
||||
|
||||
/** Alliance station ID. */
|
||||
public enum AllianceStationID {
|
||||
/** Unknown. */
|
||||
Unknown,
|
||||
/** Red 1. */
|
||||
Red1,
|
||||
/** Red 2. */
|
||||
Red2,
|
||||
/** Red 3. */
|
||||
Red3,
|
||||
/** Blue 1. */
|
||||
Blue1,
|
||||
/** Blue 2. */
|
||||
Blue2,
|
||||
/** Blue 3. */
|
||||
Blue3
|
||||
}
|
||||
|
||||
@@ -126,4 +126,7 @@ public class AnalogGyroJNI extends JNIWrapper {
|
||||
* @see "HAL_GetAnalogGyroCenter"
|
||||
*/
|
||||
public static native int getAnalogGyroCenter(int handle);
|
||||
|
||||
/** Utility class. */
|
||||
private AnalogGyroJNI() {}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,16 @@ public class AnalogJNI extends JNIWrapper {
|
||||
*/
|
||||
public static native boolean checkAnalogInputChannel(int channel);
|
||||
|
||||
/**
|
||||
* Checks that the analog output channel number is valid.
|
||||
*
|
||||
* <p>Verifies that the analog channel number is one of the legal channel numbers. Channel numbers
|
||||
* are 0-based.
|
||||
*
|
||||
* @param channel The analog output channel number.
|
||||
* @return Analog channel is valid
|
||||
* @see "HAL_CheckAnalogOutputChannel"
|
||||
*/
|
||||
public static native boolean checkAnalogOutputChannel(int channel);
|
||||
|
||||
/**
|
||||
@@ -95,8 +105,22 @@ public class AnalogJNI extends JNIWrapper {
|
||||
*/
|
||||
public static native void setAnalogInputSimDevice(int handle, int device);
|
||||
|
||||
/**
|
||||
* Sets an analog output value.
|
||||
*
|
||||
* @param portHandle the analog output handle
|
||||
* @param voltage the voltage (0-5v) to output
|
||||
* @see "HAL_SetAnalogOutput"
|
||||
*/
|
||||
public static native void setAnalogOutput(int portHandle, double voltage);
|
||||
|
||||
/**
|
||||
* Gets the current analog output value.
|
||||
*
|
||||
* @param portHandle the analog output handle
|
||||
* @return the current output voltage (0-5v)
|
||||
* @see "HAL_GetAnalogOutput"
|
||||
*/
|
||||
public static native double getAnalogOutput(int portHandle);
|
||||
|
||||
/**
|
||||
@@ -497,4 +521,7 @@ public class AnalogJNI extends JNIWrapper {
|
||||
* @see "HAL_GetAnalogTriggerFPGAIndex"
|
||||
*/
|
||||
public static native int getAnalogTriggerFPGAIndex(int analogTriggerHandle);
|
||||
|
||||
/** Utility class. */
|
||||
private AnalogJNI() {}
|
||||
}
|
||||
|
||||
@@ -177,4 +177,7 @@ public class CANAPIJNI extends JNIWrapper {
|
||||
*/
|
||||
public static native boolean readCANPacketTimeout(
|
||||
int handle, int apiId, int timeoutMs, CANData data);
|
||||
|
||||
/** Utility class. */
|
||||
private CANAPIJNI() {}
|
||||
}
|
||||
|
||||
117
hal/src/main/java/edu/wpi/first/hal/CANAPITypes.java
Normal file
117
hal/src/main/java/edu/wpi/first/hal/CANAPITypes.java
Normal file
@@ -0,0 +1,117 @@
|
||||
// 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.hal;
|
||||
|
||||
/**
|
||||
* CAN API Types.
|
||||
*
|
||||
* <p>This class defines enums for CAN device types and manufacturer IDs as specified in the WPILib
|
||||
* documentation: https://docs.wpilib.org/en/stable/docs/software/can-devices/can-addressing.html
|
||||
*/
|
||||
@SuppressWarnings("PMD.MissingStaticMethodInNonInstantiatableClass")
|
||||
public final class CANAPITypes {
|
||||
/**
|
||||
* FRC CAN device type.
|
||||
*
|
||||
* <p>This enum represents different types of CAN devices. Teams are encouraged to use the
|
||||
* kMiscellaneous for custom or miscellaneous devices.
|
||||
*
|
||||
* @see <a href=
|
||||
* "https://docs.wpilib.org/en/stable/docs/software/can-devices/can-addressing.html">CAN
|
||||
* Device Types</a>
|
||||
*/
|
||||
public enum CANDeviceType {
|
||||
/** Broadcast. */
|
||||
kBroadcast(0),
|
||||
/** Robot controller. */
|
||||
kRobotController(1),
|
||||
/** Motor controller. */
|
||||
kMotorController(2),
|
||||
/** Relay controller. */
|
||||
kRelayController(3),
|
||||
/** Gyro sensor. */
|
||||
kGyroSensor(4),
|
||||
/** Accelerometer. */
|
||||
kAccelerometer(5),
|
||||
/** Ultrasonic sensor. */
|
||||
kUltrasonicSensor(6),
|
||||
/** Gear tooth sensor. */
|
||||
kGearToothSensor(7),
|
||||
/** Power distribution. */
|
||||
kPowerDistribution(8),
|
||||
/** Pneumatics. */
|
||||
kPneumatics(9),
|
||||
/** Miscellaneous. */
|
||||
kMiscellaneous(10),
|
||||
/** IO breakout. */
|
||||
kIOBreakout(11),
|
||||
/** Firmware update. */
|
||||
kFirmwareUpdate(31);
|
||||
|
||||
@SuppressWarnings("PMD.MemberName")
|
||||
public final int id;
|
||||
|
||||
CANDeviceType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FRC CAN manufacturer ID.
|
||||
*
|
||||
* <p>This enum represents different manufacturer IDs for CAN devices. Teams are encouraged to use
|
||||
* the kTeamUse manufacturer ID for custom or team-specific devices.
|
||||
*
|
||||
* @see <a href=
|
||||
* "https://docs.wpilib.org/en/stable/docs/software/can-devices/can-addressing.html">CAN
|
||||
* Manufacturer IDs</a>
|
||||
*/
|
||||
public enum CANManufacturer {
|
||||
/** Broadcast. */
|
||||
kBroadcast(0),
|
||||
/** National Instruments. */
|
||||
kNI(1),
|
||||
/** Luminary Micro. */
|
||||
kLM(2),
|
||||
/** DEKA Research and Development Corp. */
|
||||
kDEKA(3),
|
||||
/** Cross the Road Electronics. */
|
||||
kCTRE(4),
|
||||
/** REV Robotics. */
|
||||
kREV(5),
|
||||
/** Grapple. */
|
||||
kGrapple(6),
|
||||
/** MindSensors. */
|
||||
kMS(7),
|
||||
/** Team use. */
|
||||
kTeamUse(8),
|
||||
/** Kauai Labs. */
|
||||
kKauaiLabs(9),
|
||||
/** Copperforge. */
|
||||
kCopperforge(10),
|
||||
/** Playing With Fusion. */
|
||||
kPWF(11),
|
||||
/** Studica. */
|
||||
kStudica(12),
|
||||
/** TheThriftyBot. */
|
||||
kTheThriftyBot(13),
|
||||
/** Redux Robotics. */
|
||||
kReduxRobotics(14),
|
||||
/** AndyMark. */
|
||||
kAndyMark(15),
|
||||
/** Vivid-Hosting. */
|
||||
kVividHosting(16);
|
||||
|
||||
@SuppressWarnings("PMD.MemberName")
|
||||
public final int id;
|
||||
|
||||
CANManufacturer(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
/** Utility class. */
|
||||
private CANAPITypes() {}
|
||||
}
|
||||
@@ -15,6 +15,9 @@ public class CANData {
|
||||
/** CAN frame timestamp in milliseconds. */
|
||||
public long timestamp;
|
||||
|
||||
/** Default constructor. */
|
||||
public CANData() {}
|
||||
|
||||
/**
|
||||
* API used from JNI to set the data.
|
||||
*
|
||||
|
||||
@@ -17,6 +17,9 @@ public class CANStreamMessage {
|
||||
@SuppressWarnings("MemberName")
|
||||
public int messageID;
|
||||
|
||||
/** Default constructor. */
|
||||
public CANStreamMessage() {}
|
||||
|
||||
/**
|
||||
* API used from JNI to set the data.
|
||||
*
|
||||
|
||||
@@ -221,4 +221,7 @@ public class CTREPCMJNI extends JNIWrapper {
|
||||
* @see "HAL_SetCTREPCMOneShotDuration"
|
||||
*/
|
||||
public static native void setOneShotDuration(int handle, int index, int durMs);
|
||||
|
||||
/** Utility class. */
|
||||
private CTREPCMJNI() {}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,7 @@ public class ConstantsJNI extends JNIWrapper {
|
||||
* @see "HAL_GetSystemClockTicksPerMicrosecond"
|
||||
*/
|
||||
public static native int getSystemClockTicksPerMicrosecond();
|
||||
|
||||
/** Utility class. */
|
||||
private ConstantsJNI() {}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ public class ControlWord {
|
||||
private boolean m_fmsAttached;
|
||||
private boolean m_dsAttached;
|
||||
|
||||
/** Default constructor. */
|
||||
public ControlWord() {}
|
||||
|
||||
void update(
|
||||
boolean enabled,
|
||||
boolean autonomous,
|
||||
|
||||
@@ -279,4 +279,7 @@ public class CounterJNI extends JNIWrapper {
|
||||
* @see "HAL_SetCounterReverseDirection"
|
||||
*/
|
||||
public static native void setCounterReverseDirection(int counterHandle, boolean reverseDirection);
|
||||
|
||||
/** Utility class. */
|
||||
private CounterJNI() {}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user