diff --git a/.clang-tidy b/.clang-tidy index 6f1ae3a6d57..e8fe57b952a 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -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, diff --git a/.gitattributes b/.gitattributes index 483e5c1a916..ab6ca853b10 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,7 @@ +*.cpp text eol=lf *.gradle text eol=lf +*.h text eol=lf +*.inc text eol=lf *.java text eol=lf *.json text eol=lf *.md text eol=lf diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index a101b4a423b..6db3659e1ad 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -40,7 +40,7 @@ jobs: - name: Set up Python 3.8 (macOS) if: runner.os == 'macOS' - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 diff --git a/.github/workflows/comment-command.yml b/.github/workflows/comment-command.yml index 7dd0ad670d0..abb7acda929 100644 --- a/.github/workflows/comment-command.yml +++ b/.github/workflows/comment-command.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: React Rocket - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const {owner, repo} = context.issue @@ -34,14 +34,14 @@ jobs: GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}" NUMBER: ${{ github.event.issue.number }} - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' - java-version: 11 + java-version: 17 - name: Install wpiformat run: pip3 install wpiformat - name: Run wpiformat diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index b618ff8f086..c1d14c1d8bc 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -20,7 +20,7 @@ jobs: with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 13 diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 7ed26edd893..fa17ad13915 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -70,13 +70,13 @@ jobs: artifact-name: Win64Debug architecture: x64 task: "build" - build-options: "-PciDebugOnly --max-workers 1" + build-options: "-PciDebugOnly" outputs: "build/allOutputs" build-dir: "c:\\work" - os: windows-2022 artifact-name: Win64Release architecture: x64 - build-options: "-PciReleaseOnly --max-workers 1" + build-options: "-PciReleaseOnly" task: "copyAllOutputs" outputs: "build/allOutputs" build-dir: "c:\\work" @@ -84,13 +84,13 @@ jobs: artifact-name: WinArm64Debug architecture: x64 task: "build" - build-options: "-PciDebugOnly -Pbuildwinarm64 -Ponlywindowsarm64 --max-workers 1" + build-options: "-PciDebugOnly -Pbuildwinarm64 -Ponlywindowsarm64" outputs: "build/allOutputs" build-dir: "c:\\work" - os: windows-2022 artifact-name: WinArm64Release architecture: x64 - build-options: "-PciReleaseOnly -Pbuildwinarm64 -Ponlywindowsarm64 --max-workers 1" + build-options: "-PciReleaseOnly -Pbuildwinarm64 -Ponlywindowsarm64" task: "copyAllOutputs" outputs: "build/allOutputs" build-dir: "c:\\work" @@ -112,7 +112,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 17 @@ -171,10 +171,10 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: 'zulu' - java-version: 13 + java-version: 17 - name: Set release environment variable run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV if: startsWith(github.ref, 'refs/tags/v') @@ -230,7 +230,7 @@ jobs: run: | cat combiner/products/build/allOutputs/version.txt test -s combiner/products/build/allOutputs/version.txt - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 if: | github.repository_owner == 'wpilibsuite' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) diff --git a/.github/workflows/lint-format.yml b/.github/workflows/lint-format.yml index 53de1730da5..436f1650763 100644 --- a/.github/workflows/lint-format.yml +++ b/.github/workflows/lint-format.yml @@ -23,7 +23,7 @@ jobs: git checkout -b pr git branch -f main origin/main - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 - name: Install wpiformat @@ -62,7 +62,7 @@ jobs: git checkout -b pr git branch -f main origin/main - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 - name: Install wpiformat @@ -105,9 +105,9 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: 'zulu' - java-version: 13 + java-version: 17 - name: Build with Gradle run: ./gradlew docs:zipDocs -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }} diff --git a/.github/workflows/pregenerate.yml b/.github/workflows/pregenerate.yml index fb851492514..afcf35c61ad 100644 --- a/.github/workflows/pregenerate.yml +++ b/.github/workflows/pregenerate.yml @@ -19,7 +19,7 @@ jobs: with: fetch-depth: 0 - name: Set up Python 3.9 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install jinja diff --git a/.github/workflows/upstream-utils.yml b/.github/workflows/upstream-utils.yml index afaffd53568..5d96b40d069 100644 --- a/.github/workflows/upstream-utils.yml +++ b/.github/workflows/upstream-utils.yml @@ -23,7 +23,7 @@ jobs: git checkout -b pr git branch -f main origin/main - name: Set up Python 3.9 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 - name: Configure committer identity diff --git a/CMakeLists.txt b/CMakeLists.txt index c688bba3b24..79df35974bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) @@ -214,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)") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 763521a0220..6680779707c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,11 +12,11 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont ## General Contribution Rules -- Everything in the library must work for the 3000+ teams that will be using it. +- Everything in the library must work for the 4000+ teams that will be using it. - We need to be able to maintain submitted changes, even if you are no longer working on the project. - Tool suite changes must be generally useful to a broad range of teams - Excluding bug fixes, changes in one language generally need to have corresponding changes in other languages. - - Some features, such the addition of C++11 for WPILibC or Functional Interfaces for WPILibJ, are specific to that version of WPILib only. + - Some features, such the addition of C++23 for WPILibC or Functional Interfaces for WPILibJ, are specific to that version of WPILib only. New language features added to C++ must be wrappable in Python for [RobotPy](https://github.com/robotpy). - Substantial changes often need to have corresponding LabVIEW changes. To do this, we will work with NI on these large changes. - Changes should have tests. - Code should be well documented. @@ -27,7 +27,8 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont - Bug reports and fixes - We will generally accept bug fixes without too much question. If they are only implemented for one language, we will implement them for any other necessary languages. Bug reports are also welcome, please submit them to our GitHub issue tracker. - While we do welcome improvements to the API, there are a few important rules to consider: - - Features must be added to both WPILibC and WPILibJ, with rare exceptions. + - Features must be added to Java (WPILibJ), C++ (WPILibC), with rare exceptions. + - Most of Python (RobotPy) is created by wrapping WPILibC with pybind11 via robotpy-build. However, new features to the command framework should also be submitted to [robotpy-commands-v2](https://github.com/robotpy/robotpy-commands-v2) as the command framework is reimplemented in Python. - During competition season, we will not merge any new feature additions. We want to ensure that the API is stable during the season to help minimize issues for teams. - Ask about large changes before spending a bunch of time on them! You can create a new issue on our GitHub tracker for feature request/discussion and talk about it with us there. - Features that make it easier for teams with less experience to be more successful are more likely to be accepted. diff --git a/LICENSE.md b/LICENSE.md index 43b62ec2e32..645e54253ad 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2009-2023 FIRST and other WPILib contributors +Copyright (c) 2009-2024 FIRST and other WPILib contributors All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/MAINTAINERS.md b/MAINTAINERS.md index d4c34909b20..483130ccf6a 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -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. diff --git a/MavenArtifacts.md b/MavenArtifacts.md index 655f9c5a363..9870028183a 100644 --- a/MavenArtifacts.md +++ b/MavenArtifacts.md @@ -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 diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 3eaf7b99889..3fc55565adb 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -1204,3 +1204,8 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +================ +2024 Field Image +================ +2024 Field Image from MikLast: https://www.chiefdelphi.com/t/2024-crescendo-top-down-field-renders/447764 diff --git a/apriltag/README.md b/apriltag/README.md new file mode 100644 index 00000000000..5bf1d6778ed --- /dev/null +++ b/apriltag/README.md @@ -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()` diff --git a/apriltag/convert_apriltag_layouts.py b/apriltag/convert_apriltag_layouts.py new file mode 100755 index 00000000000..2c1d60e411d --- /dev/null +++ b/apriltag/convert_apriltag_layouts.py @@ -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() diff --git a/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTag.java b/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTag.java index 2c9be4a6866..9ee0207fedc 100644 --- a/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTag.java +++ b/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTag.java @@ -11,14 +11,23 @@ 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( diff --git a/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagDetector.java b/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagDetector.java index c62edfa5076..a0774da171a 100644 --- a/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagDetector.java +++ b/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagDetector.java @@ -57,8 +57,21 @@ public static class Config { */ 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 static class QuadThresholdParameters { */ 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 boolean equals(Object obj) { } } + /** Constructs an AprilTagDetector. */ public AprilTagDetector() { m_native = AprilTagJNI.createDetector(); } diff --git a/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFieldLayout.java b/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFieldLayout.java index c4b1eb8de85..5d0e4248878 100644 --- a/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFieldLayout.java +++ b/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFieldLayout.java @@ -44,8 +44,11 @@ @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, } diff --git a/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFields.java b/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFields.java index dda8d343c52..653151e6266 100644 --- a/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFields.java +++ b/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagFields.java @@ -7,15 +7,22 @@ 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) { diff --git a/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagPoseEstimator.java b/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagPoseEstimator.java index 2b7f68a5267..182f394a319 100644 --- a/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagPoseEstimator.java +++ b/apriltag/src/main/java/edu/wpi/first/apriltag/AprilTagPoseEstimator.java @@ -29,10 +29,19 @@ public Config(double tagSize, double fx, double fy, double cx, double cy) { 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 diff --git a/apriltag/src/main/java/edu/wpi/first/apriltag/jni/AprilTagJNI.java b/apriltag/src/main/java/edu/wpi/first/apriltag/jni/AprilTagJNI.java index d03169b8b96..6aabbf9ddad 100644 --- a/apriltag/src/main/java/edu/wpi/first/apriltag/jni/AprilTagJNI.java +++ b/apriltag/src/main/java/edu/wpi/first/apriltag/jni/AprilTagJNI.java @@ -13,21 +13,36 @@ import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; +/** AprilTag JNI. */ public class AprilTagJNI { static boolean libraryLoaded = false; static RuntimeLoader 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 { @@ -191,7 +206,24 @@ public static native Transform3d estimatePose( double cx, double cy); + /** + * 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); + /** + * 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() {} } diff --git a/apriltag/src/main/native/cpp/AprilTagFields.cpp b/apriltag/src/main/native/cpp/AprilTagFields.cpp index 2f929378ba2..be512caa00b 100644 --- a/apriltag/src/main/native/cpp/AprilTagFields.cpp +++ b/apriltag/src/main/native/cpp/AprilTagFields.cpp @@ -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"); } diff --git a/apriltag/src/main/native/include/frc/apriltag/AprilTag.h b/apriltag/src/main/native/include/frc/apriltag/AprilTag.h index abb8ac59371..a9ef78cc458 100644 --- a/apriltag/src/main/native/include/frc/apriltag/AprilTag.h +++ b/apriltag/src/main/native/include/frc/apriltag/AprilTag.h @@ -12,14 +12,16 @@ 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 bool Generate36h11AprilTagImage(wpi::RawFrame* frame, int id); diff --git a/apriltag/src/main/native/include/frc/apriltag/AprilTagFieldLayout.h b/apriltag/src/main/native/include/frc/apriltag/AprilTagFieldLayout.h index 2b57d2ce4aa..1f5397b25a2 100644 --- a/apriltag/src/main/native/include/frc/apriltag/AprilTagFieldLayout.h +++ b/apriltag/src/main/native/include/frc/apriltag/AprilTagFieldLayout.h @@ -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, }; diff --git a/apriltag/src/main/native/include/frc/apriltag/AprilTagFields.h b/apriltag/src/main/native/include/frc/apriltag/AprilTagFields.h index 4bab29919c7..933cf287ae3 100644 --- a/apriltag/src/main/native/include/frc/apriltag/AprilTagFields.h +++ b/apriltag/src/main/native/include/frc/apriltag/AprilTagFields.h @@ -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 diff --git a/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2024-crescendo.csv b/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2024-crescendo.csv new file mode 100644 index 00000000000..cf570ce1b51 --- /dev/null +++ b/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2024-crescendo.csv @@ -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 diff --git a/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2024-crescendo.json b/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2024-crescendo.json new file mode 100644 index 00000000000..0da1bac4049 --- /dev/null +++ b/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2024-crescendo.json @@ -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 + } +} diff --git a/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServer.java b/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServer.java index 24f2ba28d01..5597f8002e6 100644 --- a/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServer.java +++ b/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServer.java @@ -27,6 +27,7 @@ import edu.wpi.first.networktables.StringEntry; import edu.wpi.first.networktables.StringPublisher; import edu.wpi.first.util.PixelFormat; +import java.lang.ref.Reference; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -39,6 +40,7 @@ * NetworkTables. */ public final class CameraServer { + /** CameraServer base port. */ public static final int kBasePort = 1181; private static final String kPublishName = "/CameraPublisher"; @@ -139,6 +141,7 @@ public void close() { if (m_choicesPublisher != null) { m_choicesPublisher.close(); } + Reference.reachabilityFence(m_videoListener); } BooleanEntry m_booleanValueEntry; @@ -222,7 +225,7 @@ public void close() throws Exception { // - "PropertyInfo/{Property}" - Property supporting information // Listener for video events - @SuppressWarnings({"PMD.UnusedPrivateField", "PMD.AvoidCatchingGenericException"}) + @SuppressWarnings("PMD.AvoidCatchingGenericException") private static final VideoListener m_videoListener = new VideoListener( event -> { diff --git a/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServerShared.java b/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServerShared.java index 4726de184fd..dfa570f85c2 100644 --- a/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServerShared.java +++ b/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServerShared.java @@ -4,6 +4,7 @@ package edu.wpi.first.cameraserver; +/** CameraServer shared functions. */ public interface CameraServerShared { /** * get the main thread id func. diff --git a/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServerSharedStore.java b/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServerSharedStore.java index 3d9e1194066..b129d55291d 100644 --- a/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServerSharedStore.java +++ b/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServerSharedStore.java @@ -4,6 +4,7 @@ package edu.wpi.first.cameraserver; +/** Storage for CameraServerShared instance. */ public final class CameraServerSharedStore { private static CameraServerShared cameraServerShared; diff --git a/cameraserver/src/main/java/edu/wpi/first/vision/VisionRunner.java b/cameraserver/src/main/java/edu/wpi/first/vision/VisionRunner.java index ab7072f6c99..b9459cb00c4 100644 --- a/cameraserver/src/main/java/edu/wpi/first/vision/VisionRunner.java +++ b/cameraserver/src/main/java/edu/wpi/first/vision/VisionRunner.java @@ -14,6 +14,7 @@ * 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

Vision pipeline type. * @see VisionPipeline * @see VisionThread * @see vision diff --git a/cameraserver/src/main/native/include/cameraserver/CameraServer.h b/cameraserver/src/main/native/include/cameraserver/CameraServer.h index 5553710a0dd..09606128bd1 100644 --- a/cameraserver/src/main/native/include/cameraserver/CameraServer.h +++ b/cameraserver/src/main/native/include/cameraserver/CameraServer.h @@ -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. diff --git a/cscore/cscore-config.cmake.in b/cscore/cscore-config.cmake.in index da5a71f696f..b9f28615113 100644 --- a/cscore/cscore-config.cmake.in +++ b/cscore/cscore-config.cmake.in @@ -1,6 +1,7 @@ include(CMakeFindDependencyMacro) @FILENAME_DEP_REPLACE@ @WPIUTIL_DEP_REPLACE@ +@WPINET_DEP_REPLACE@ find_dependency(OpenCV) @FILENAME_DEP_REPLACE@ diff --git a/cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java b/cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java index 2259582f847..26933d41bbd 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java @@ -9,21 +9,36 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.opencv.core.Core; +/** CameraServer CV JNI. */ public class CameraServerCvJNI { static boolean libraryLoaded = false; static RuntimeLoader 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 { @@ -59,17 +74,64 @@ public static synchronized void forceLoad() throws IOException { libraryLoaded = true; } + /** + * Creates a CV source. + * + * @param name Name. + * @param pixelFormat OpenCV pixel format. + * @param width Image width. + * @param height Image height. + * @param fps Frames per second. + * @return CV source. + */ public static native int createCvSource( String name, int pixelFormat, int width, int height, int fps); + /** + * Put source frame. + * + * @param source Source handle. + * @param imageNativeObj Image native object handle. + */ public static native void putSourceFrame(int source, long imageNativeObj); + /** + * Creates a CV sink. + * + * @param name Name. + * @param pixelFormat OpenCV pixel format. + * @return CV sink handle. + */ public static native int createCvSink(String name, int pixelFormat); + // /** + // * Creates a CV sink callback. + // * + // * @param name Name. + // * @param processFrame Process frame callback. + // */ // public static native int createCvSinkCallback(String name, // void (*processFrame)(long time)); + /** + * Returns sink frame handle. + * + * @param sink Sink handle. + * @param imageNativeObj Image native object handle. + * @return Sink frame handle. + */ public static native long grabSinkFrame(int sink, long imageNativeObj); + /** + * Returns sink frame timeout in microseconds. + * + * @param sink Sink handle. + * @param imageNativeObj Image native object handle. + * @param timeout Timeout in seconds. + * @return Sink frame timeout in microseconds. + */ public static native long grabSinkFrameTimeout(int sink, long imageNativeObj, double timeout); + + /** Utility class. */ + private CameraServerCvJNI() {} } diff --git a/cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java b/cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java index ae075bc83a6..d3b28cfb291 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java @@ -11,21 +11,36 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; +/** CameraServer JNI. */ public class CameraServerJNI { static boolean libraryLoaded = false; static RuntimeLoader 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 { @@ -62,140 +77,521 @@ public static synchronized void forceLoad() throws IOException { // // Property Functions // + + /** + * Returns property kind. + * + * @param property Property handle. + * @return Property kind. + */ public static native int getPropertyKind(int property); + /** + * Returns property name. + * + * @param property Property handle. + * @return Property name. + */ public static native String getPropertyName(int property); + /** + * Returns property value. + * + * @param property Property handle. + * @return Property value. + */ public static native int getProperty(int property); + /** + * Sets property value. + * + * @param property Property handle. + * @param value Property value. + */ public static native void setProperty(int property, int value); + /** + * Returns property minimum. + * + * @param property Property handle. + * @return Property minimum. + */ public static native int getPropertyMin(int property); + /** + * Returns property maximum. + * + * @param property Property handle. + * @return Property maximum. + */ public static native int getPropertyMax(int property); + /** + * Returns property step. + * + * @param property Property handle. + * @return Property step. + */ public static native int getPropertyStep(int property); + /** + * Returns property default value. + * + * @param property Property handle. + * @return Property default value. + */ public static native int getPropertyDefault(int property); + /** + * Returns property value as a string. + * + * @param property Property handle. + * @return Property value as a string. + */ public static native String getStringProperty(int property); + /** + * Sets property value to a string. + * + * @param property Property handle. + * @param value Property value string. + */ public static native void setStringProperty(int property, String value); + /** + * Returns enum of possible property value strings. + * + * @param property Property handle. + * @return Enum of possible property value strings. + */ public static native String[] getEnumPropertyChoices(int property); // // Source Creation Functions // + + /** + * Creates a new USB camera by device. + * + * @param name USB camera name. + * @param dev USB camera device number. + * @return USB camera handle. + */ public static native int createUsbCameraDev(String name, int dev); + /** + * Creates a new USB camera by path. + * + * @param name USB camera name. + * @param path USB camera path. + * @return USB camera handle. + */ public static native int createUsbCameraPath(String name, String path); + /** + * Creates an HTTP camera. + * + * @param name HTTP camera name. + * @param url HTTP camera stream URL. + * @param kind HTTP camera kind. + * @return HTTP camera handle. + */ public static native int createHttpCamera(String name, String url, int kind); + /** + * Creates an HTTP camera from multiple URLs. + * + * @param name HTTP camera name. + * @param urls HTTP camera stream URLs. + * @param kind HTTP camera kind. + * @return HTTP camera handle. + */ public static native int createHttpCameraMulti(String name, String[] urls, int kind); + /** + * Creates a raw source. + * + * @param name Source name. + * @param pixelFormat Pixel format. + * @param width Image width. + * @param height Image height. + * @param fps Source frames per second. + * @return Raw source handle. + */ public static native int createRawSource( String name, int pixelFormat, int width, int height, int fps); // // Source Functions // + + /** + * Returns source kind. + * + * @param source Source handle. + * @return Source kind. + */ public static native int getSourceKind(int source); + /** + * Returns source name. + * + * @param source Source handle. + * @return Source name. + */ public static native String getSourceName(int source); + /** + * Returns source description. + * + * @param source Source handle. + * @return Source description. + */ public static native String getSourceDescription(int source); + /** + * Returns source's last frame time. + * + * @param source Source handle. + * @return Source's last frame time. + */ public static native long getSourceLastFrameTime(int source); + /** + * Sets source connection strategy. + * + * @param source Source handle. + * @param strategy Connection strategy. + */ public static native void setSourceConnectionStrategy(int source, int strategy); + /** + * Returns true if source is connected. + * + * @param source Source handle. + * @return True if source is connected. + */ public static native boolean isSourceConnected(int source); + /** + * Returns true if source is enabled. + * + * @param source Source handle. + * @return True if source is enabled. + */ public static native boolean isSourceEnabled(int source); + /** + * Returns source property. + * + * @param source Source handle. + * @param name Source property name. + * @return Source property. + */ public static native int getSourceProperty(int source, String name); + /** + * Returns list of source property handles. + * + * @param source Source handle. + * @return List of source property handles. + */ public static native int[] enumerateSourceProperties(int source); + /** + * Returns source video mode. + * + * @param source Source handle. + * @return Source video mode. + */ public static native VideoMode getSourceVideoMode(int source); + /** + * Sets source video mode. + * + * @param source Source handle. + * @param pixelFormat Pixel format. + * @param width Image width. + * @param height Image height. + * @param fps Source frames per second. + * @return True if set succeeded. + */ public static native boolean setSourceVideoMode( int source, int pixelFormat, int width, int height, int fps); + /** + * Sets source pixel format. + * + * @param source Source handle. + * @param pixelFormat Source pixel format. + * @return True if set succeeded. + */ public static native boolean setSourcePixelFormat(int source, int pixelFormat); + /** + * Sets source resolution. + * + * @param source Source handle. + * @param width Image width. + * @param height Image height. + * @return True if set succeeded. + */ public static native boolean setSourceResolution(int source, int width, int height); + /** + * Sets source FPS. + * + * @param source Source handle. + * @param fps Source frames per second. + * @return True if set succeeded. + */ public static native boolean setSourceFPS(int source, int fps); + /** + * Sets source configuration JSON. + * + * @param source Source handle. + * @param config Configuration JSON. + * @return True if set succeeded. + */ public static native boolean setSourceConfigJson(int source, String config); + /** + * Returns source configuration JSON. + * + * @param source Source handle. + * @return Source configuration JSON. + */ public static native String getSourceConfigJson(int source); + /** + * Returns list of source's supported video modes. + * + * @param source Source handle. + * @return List of source's supported video modes. + */ public static native VideoMode[] enumerateSourceVideoModes(int source); + /** + * Returns list of source sinks. + * + * @param source Source handle. + * @return List of source sinks. + */ public static native int[] enumerateSourceSinks(int source); + /** + * Copies source. + * + * @param source Source handle. + * @return New source handle. + */ public static native int copySource(int source); + /** + * Releases source. + * + * @param source Source handle. + */ public static native void releaseSource(int source); // // Camera Source Common Property Functions // + + /** + * Sets camera brightness. + * + * @param source Source handle. + * @param brightness Brightness. + */ public static native void setCameraBrightness(int source, int brightness); + /** + * Returns camera brightness. + * + * @param source Source handle. + * @return Camera brightness. + */ public static native int getCameraBrightness(int source); + /** + * Sets camera white balance to auto. + * + * @param source Source handle. + */ public static native void setCameraWhiteBalanceAuto(int source); + /** + * Sets camera white balance to "hold current". + * + * @param source Source handle. + */ public static native void setCameraWhiteBalanceHoldCurrent(int source); + /** + * Sets camera white balance to the given value. + * + * @param source Source handle. + * @param value White balance. + */ public static native void setCameraWhiteBalanceManual(int source, int value); + /** + * Sets camera exposure to auto. + * + * @param source Source handle. + */ public static native void setCameraExposureAuto(int source); + /** + * Sets camera exposure to "hold current". + * + * @param source Source handle. + */ public static native void setCameraExposureHoldCurrent(int source); + /** + * Sets camera exposure to the given value. + * + * @param source Source handle. + * @param value Exposure. + */ public static native void setCameraExposureManual(int source, int value); // // UsbCamera Source Functions // + + /** + * Sets USB camera path. + * + * @param source Source handle. + * @param path USB camera path. + */ public static native void setUsbCameraPath(int source, String path); + /** + * Returns USB camera path. + * + * @param source Source handle. + * @return USB camera path. + */ public static native String getUsbCameraPath(int source); + /** + * Returns USB camera info. + * + * @param source Source handle. + * @return USB camera info. + */ public static native UsbCameraInfo getUsbCameraInfo(int source); // // HttpCamera Source Functions // + + /** + * Returns HTTP camera kind. + * + * @param source Source handle. + * @return HTTP camera kind. + */ public static native int getHttpCameraKind(int source); + /** + * Sets HTTP camera URLs. + * + * @param source Source handle. + * @param urls HTTP camera URLs. + */ public static native void setHttpCameraUrls(int source, String[] urls); + /** + * Returns HTTP camera URLs. + * + * @param source Source handle. + * @return HTTP camera URLs. + */ public static native String[] getHttpCameraUrls(int source); // // Image Source Functions // + + /** + * Puts raw frame into source. + * + * @param source Source handle. + * @param frame Frame handle. + */ public static native void putRawSourceFrame(int source, long frame); + /** + * Puts raw frame into source. + * + * @param source Source handle. + * @param data Frame byte buffer. + * @param size Frame size. + * @param width Frame width. + * @param height Frame height. + * @param stride Frame stride. + * @param pixelFormat Frame pixel format. + */ public static native void putRawSourceFrameBB( int source, ByteBuffer data, int size, int width, int height, int stride, int pixelFormat); + /** + * Puts raw frame into source. + * + * @param source Source handle. + * @param data Frame handle. + * @param size Frame size. + * @param width Frame width. + * @param height Frame height. + * @param stride Frame stride. + * @param pixelFormat Frame pixel format. + */ public static native void putRawSourceFrameData( int source, long data, int size, int width, int height, int stride, int pixelFormat); + /** + * Notify source error. + * + * @param source Source handle. + * @param msg Error message. + */ public static native void notifySourceError(int source, String msg); + /** + * Sets whether source is connected. + * + * @param source Source handle. + * @param connected True if source is connected. + */ public static native void setSourceConnected(int source, boolean connected); + /** + * Sets source description. + * + * @param source Source handle. + * @param description Source description. + */ public static native void setSourceDescription(int source, String description); + /** + * Creates a source property. + * + * @param source Source handle. + * @param name Property name. + * @param kind Property kind. + * @param minimum Property minimum. + * @param maximum Property maximum. + * @param step Property step. + * @param defaultValue Property default value. + * @param value Property value. + * @return Source property handle. + */ public static native int createSourceProperty( int source, String name, @@ -206,90 +602,292 @@ public static native int createSourceProperty( int defaultValue, int value); + /** + * Sets list of possible property values. + * + * @param source Source handle. + * @param property Property handle. + * @param choices List of possible property values. + */ public static native void setSourceEnumPropertyChoices( int source, int property, String[] choices); // // Sink Creation Functions // + + /** + * Creates an MJPEG server. + * + * @param name MJPEG server name. + * @param listenAddress IP address at which server should listen. + * @param port Port on which server should listen. + * @return MJPEG server handle. + */ public static native int createMjpegServer(String name, String listenAddress, int port); + /** + * Creates a raw sink. + * + * @param name Sink name. + * @return Raw sink handle. + */ public static native int createRawSink(String name); // // Sink Functions // + + /** + * Returns sink kind. + * + * @param sink Sink handle. + * @return Sink kind. + */ public static native int getSinkKind(int sink); + /** + * Returns sink name. + * + * @param sink Sink handle. + * @return Sink name. + */ public static native String getSinkName(int sink); + /** + * Returns sink description. + * + * @param sink Sink handle. + * @return Sink description. + */ public static native String getSinkDescription(int sink); + /** + * Returns sink property. + * + * @param sink Sink handle. + * @param name Property name. + * @return Sink property handle. + */ public static native int getSinkProperty(int sink, String name); + /** + * Returns list of sink property handles. + * + * @param sink Sink handle. + * @return List of sink property handles. + */ public static native int[] enumerateSinkProperties(int sink); + /** + * Sets sink configuration JSON. + * + * @param sink Sink handle. + * @param config Configuration JSON. + * @return True if set succeeded. + */ public static native boolean setSinkConfigJson(int sink, String config); + /** + * Returns sink configuration JSON. + * + * @param sink Sink handle. + * @return Sink configuration JSON. + */ public static native String getSinkConfigJson(int sink); + /** + * Sets sink source. + * + * @param sink Sink handle. + * @param source Source handle. + */ public static native void setSinkSource(int sink, int source); + /** + * Returns sink source property. + * + * @param sink Sink handle. + * @param name Property name. + * @return Sink source property handle. + */ public static native int getSinkSourceProperty(int sink, String name); + /** + * Returns sink source. + * + * @param sink Sink handle. + * @return Sink source handle. + */ public static native int getSinkSource(int sink); + /** + * Copies sink. + * + * @param sink Sink handle. + * @return New sink handle. + */ public static native int copySink(int sink); + /** + * Releases sink. + * + * @param sink Sink handle. + */ public static native void releaseSink(int sink); // // MjpegServer Sink Functions // + + /** + * Returns MJPEG server listen address. + * + * @param sink Sink handle. + * @return MJPEG server listen address. + */ public static native String getMjpegServerListenAddress(int sink); + /** + * Returns MJPEG server port. + * + * @param sink Sink handle. + * @return MJPEG server port. + */ public static native int getMjpegServerPort(int sink); // // Image Sink Functions // + + /** + * Sets sink description. + * + * @param sink Sink handle. + * @param description Sink description. + */ public static native void setSinkDescription(int sink, String description); + /** + * Returns raw sink frame. + * + * @param sink Sink handle. + * @param frame Raw frame. + * @param nativeObj Native object. + * @return Raw sink frame handle. + */ public static native long grabRawSinkFrame(int sink, RawFrame frame, long nativeObj); + /** + * Returns raw sink frame timeout. + * + * @param sink Sink handle. + * @param frame Raw frame. + * @param nativeObj Native object. + * @param timeout Timeout in seconds. + * @return Raw sink frame timeout. + */ public static native long grabRawSinkFrameTimeout( int sink, RawFrame frame, long nativeObj, double timeout); + /** + * Returns sink error message. + * + * @param sink Sink handle. + * @return Sink error message. + */ public static native String getSinkError(int sink); + /** + * Sets sink enable. + * + * @param sink Sink handle. + * @param enabled True if sink should be enabled. + */ public static native void setSinkEnabled(int sink, boolean enabled); // // Listener Functions // + + /** + * Adds listener. + * + * @param listener Video event callback. + * @param eventMask Event mask. + * @param immediateNotify True to immediately notify on event. + * @return Listener handle. + */ public static native int addListener( Consumer listener, int eventMask, boolean immediateNotify); + /** + * Removes listener. + * + * @param handle Listener handle. + */ public static native void removeListener(int handle); + /** + * Creates listener poller. + * + * @return Listener poller handle. + */ public static native int createListenerPoller(); + /** + * Destroys listener poller. + * + * @param poller Listener poller handle. + */ public static native void destroyListenerPoller(int poller); + /** + * Add polled listener. + * + * @param poller Poller handle. + * @param eventMask Event mask. + * @param immediateNotify True to immediately notify on event. + * @return Polled listener handle. + */ public static native int addPolledListener(int poller, int eventMask, boolean immediateNotify); + /** + * Polls listener. + * + * @param poller Poller handle. + * @return List of video events. + * @throws InterruptedException if polling was interrupted. + */ public static native VideoEvent[] pollListener(int poller) throws InterruptedException; + /** + * Polls listener with timeout. + * + * @param poller Poller handle. + * @param timeout Timeout in seconds. + * @return List of video events. + * @throws InterruptedException if polling was interrupted. + */ public static native VideoEvent[] pollListenerTimeout(int poller, double timeout) throws InterruptedException; + /** + * Cancels poll listener. + * + * @param poller Poller handle. + */ public static native void cancelPollListener(int poller); // // Telemetry Functions // + + /** Telemetry kind. */ public enum TelemetryKind { + /** kSourceBytesReceived. */ kSourceBytesReceived(1), + /** kSourceFramesReceived. */ kSourceFramesReceived(2); private final int value; @@ -298,23 +896,66 @@ public enum TelemetryKind { this.value = value; } + /** + * Returns telemetry kind value. + * + * @return Telemetry kind value. + */ public int getValue() { return value; } } + /** + * Sets telemetry period. + * + * @param seconds Telemetry period in seconds. + */ public static native void setTelemetryPeriod(double seconds); + /** + * Returns telemetry elapsed time. + * + * @return Telemetry elapsed time. + */ public static native double getTelemetryElapsedTime(); + /** + * Returns telemetry value. + * + * @param handle Telemetry handle. + * @param kind Telemetry kind. + * @return Telemetry value. + */ public static native long getTelemetryValue(int handle, int kind); + /** + * Returns telemetry value. + * + * @param handle Telemetry handle. + * @param kind Telemetry kind. + * @return Telemetry value. + */ public static long getTelemetryValue(int handle, TelemetryKind kind) { return getTelemetryValue(handle, kind.getValue()); } + /** + * Returns telemetry average value. + * + * @param handle Telemetry handle. + * @param kind Telemetry kind. + * @return Telemetry average value. + */ public static native double getTelemetryAverageValue(int handle, int kind); + /** + * Returns telemetry average value. + * + * @param handle Telemetry handle. + * @param kind Telemetry kind. + * @return Telemetry average value. + */ public static double getTelemetryAverageValue(int handle, TelemetryKind kind) { return getTelemetryAverageValue(handle, kind.getValue()); } @@ -322,29 +963,82 @@ public static double getTelemetryAverageValue(int handle, TelemetryKind kind) { // // Logging Functions // + + /** Logger functional interface. */ @FunctionalInterface public interface LoggerFunction { + /** + * Log a string. + * + * @param level Log level. + * @param file Log file. + * @param line Line number. + * @param msg Log message. + */ void apply(int level, String file, int line, String msg); } + /** + * Sets logger. + * + * @param func Logger function. + * @param minLevel Minimum logging level. + */ public static native void setLogger(LoggerFunction func, int minLevel); // // Utility Functions // + + /** + * Returns list of USB cameras. + * + * @return List of USB cameras. + */ public static native UsbCameraInfo[] enumerateUsbCameras(); + /** + * Returns list of sources. + * + * @return List of sources. + */ public static native int[] enumerateSources(); + /** + * Returns list of sinks. + * + * @return List of sinks. + */ public static native int[] enumerateSinks(); + /** + * Returns hostname. + * + * @return Hostname. + */ public static native String getHostname(); + /** + * Returns list of network interfaces. + * + * @return List of network interfaces. + */ public static native String[] getNetworkInterfaces(); + /** Runs main run loop. */ public static native void runMainRunLoop(); + /** + * Runs main run loop with timeout. + * + * @param timeoutSeconds Timeout in seconds. + * @return 3 on timeout, 2 on signal, 1 on other. + */ public static native int runMainRunLoopTimeout(double timeoutSeconds); + /** Stops main run loop. */ public static native void stopMainRunLoop(); + + /** Utility class. */ + private CameraServerJNI() {} } diff --git a/cscore/src/main/java/edu/wpi/first/cscore/HttpCamera.java b/cscore/src/main/java/edu/wpi/first/cscore/HttpCamera.java index 8c72350e635..e34bf8455d3 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/HttpCamera.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/HttpCamera.java @@ -6,10 +6,15 @@ /** A source that represents a MJPEG-over-HTTP (IP) camera. */ public class HttpCamera extends VideoCamera { + /** HTTP camera kind. */ 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; @@ -18,6 +23,11 @@ public enum HttpCameraKind { this.value = value; } + /** + * Returns HttpCameraKind value. + * + * @return HttpCameraKind value. + */ public int getValue() { return value; } diff --git a/cscore/src/main/java/edu/wpi/first/cscore/ImageSink.java b/cscore/src/main/java/edu/wpi/first/cscore/ImageSink.java index 1c4cac2f18e..701488da17b 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/ImageSink.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/ImageSink.java @@ -4,7 +4,13 @@ package edu.wpi.first.cscore; +/** A base class for single image reading sinks. */ public abstract class ImageSink extends VideoSink { + /** + * Constructs an ImageSink. + * + * @param handle The image sink handle. + */ protected ImageSink(int handle) { super(handle); } diff --git a/cscore/src/main/java/edu/wpi/first/cscore/ImageSource.java b/cscore/src/main/java/edu/wpi/first/cscore/ImageSource.java index 6bebed811a1..cde0aaca1e2 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/ImageSource.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/ImageSource.java @@ -4,7 +4,13 @@ package edu.wpi.first.cscore; +/** A base class for single image providing sources. */ public abstract class ImageSource extends VideoSource { + /** + * Constructs an ImageSource. + * + * @param handle The image source handle. + */ protected ImageSource(int handle) { super(handle); } diff --git a/cscore/src/main/java/edu/wpi/first/cscore/VideoCamera.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoCamera.java index 23cf7d6e980..089c77f7304 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/VideoCamera.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoCamera.java @@ -6,14 +6,32 @@ /** A source that represents a video camera. */ public class VideoCamera extends VideoSource { + /** White balance. */ public static class WhiteBalance { + /** Fixed indoor white balance. */ public static final int kFixedIndoor = 3000; + + /** Fixed outdoor white balance 1. */ public static final int kFixedOutdoor1 = 4000; + + /** Fixed outdoor white balance 2. */ public static final int kFixedOutdoor2 = 5000; + + /** Fixed fluorescent white balance 1. */ public static final int kFixedFluorescent1 = 5100; + + /** Fixed fluorescent white balance 2. */ public static final int kFixedFlourescent2 = 5200; + + /** Default constructor. */ + public WhiteBalance() {} } + /** + * Constructs a VideoCamera. + * + * @param handle The video camera handle. + */ protected VideoCamera(int handle) { super(handle); } diff --git a/cscore/src/main/java/edu/wpi/first/cscore/VideoEvent.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoEvent.java index 0c60d6df327..03e9a34fb52 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/VideoEvent.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoEvent.java @@ -7,27 +7,49 @@ /** Video event. */ @SuppressWarnings("MemberName") public class VideoEvent { + /** VideoEvent kind. */ 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; @@ -36,6 +58,11 @@ public enum Kind { this.value = value; } + /** + * Returns the kind value. + * + * @return The kind value. + */ public int getValue() { return value; } @@ -118,39 +145,67 @@ public static Kind getKindFromInt(int kind) { this.listener = listener; } + /** The video event kind. */ public Kind kind; - // Valid for kSource* and kSink* respectively + /** + * The source handle. + * + *

Valid for kSource* and kSink* respectively. + */ public int sourceHandle; + /** The sink handle. */ public int sinkHandle; - // Source/sink/property name + /** Source/sink/property name. */ public String name; - // Fields for kSourceVideoModeChanged event + // Fields for kSourceVideoModeChanged event. + + /** New source video mode. */ public VideoMode mode; - // Fields for kSourceProperty* events + // Fields for kSourceProperty* events. + + /** Source property handle. */ public int propertyHandle; + /** Source property kind. */ public VideoProperty.Kind propertyKind; + /** Event value. */ public int value; + /** Event value as a string. */ public String valueStr; - // Listener that was triggered + /** Listener that was triggered. */ public int listener; + /** + * Returns the source associated with the event (if any). + * + * @return The source associated with the event (if any). + */ public VideoSource getSource() { return new VideoSource(CameraServerJNI.copySource(sourceHandle)); } + /** + * Returns the sink associated with the event (if any). + * + * @return The sink associated with the event (if any). + */ public VideoSink getSink() { return new VideoSink(CameraServerJNI.copySink(sinkHandle)); } + /** + * Returns the property associated with the event (if any). + * + * @return The property associated with the event (if any). + */ public VideoProperty getProperty() { return new VideoProperty(propertyHandle, propertyKind); } diff --git a/cscore/src/main/java/edu/wpi/first/cscore/VideoException.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoException.java index 1c445c6f77f..8b481a77e22 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/VideoException.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoException.java @@ -8,6 +8,11 @@ public class VideoException extends RuntimeException { private static final long serialVersionUID = -9155939328084105145L; + /** + * Constructs the exception with the given message. + * + * @param msg The exception message. + */ public VideoException(String msg) { super(msg); } diff --git a/cscore/src/main/java/edu/wpi/first/cscore/VideoListener.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoListener.java index c95e5458be1..80236b27719 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/VideoListener.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoListener.java @@ -51,6 +51,11 @@ public synchronized void close() { } } + /** + * Returns true if the video listener handle is valid. + * + * @return True if the video listener handle is valid. + */ public boolean isValid() { return m_handle != 0; } diff --git a/cscore/src/main/java/edu/wpi/first/cscore/VideoProperty.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoProperty.java index 8179ba3c3da..6679ab0af94 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/VideoProperty.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoProperty.java @@ -6,11 +6,17 @@ /** 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; @@ -19,6 +25,11 @@ public enum Kind { this.value = value; } + /** + * Returns the Kind value. + * + * @return The Kind value. + */ public int getValue() { return value; } @@ -45,69 +56,152 @@ public static Kind getKindFromInt(int kind) { } } + /** + * Returns property name. + * + * @return Property name. + */ public String getName() { return CameraServerJNI.getPropertyName(m_handle); } + /** + * Returns property kind. + * + * @return Property kind. + */ public Kind getKind() { return m_kind; } + /** + * Returns true if property is valid. + * + * @return True if property is valid. + */ public boolean isValid() { return m_kind != Kind.kNone; } - // Kind checkers + /** + * Returns true if property is a boolean. + * + * @return True if property is a boolean. + */ public boolean isBoolean() { return m_kind == Kind.kBoolean; } + /** + * Returns true if property is an integer. + * + * @return True if property is an integer. + */ public boolean isInteger() { return m_kind == Kind.kInteger; } + /** + * Returns true if property is a string. + * + * @return True if property is a string. + */ public boolean isString() { return m_kind == Kind.kString; } + /** + * Returns true if property is an enum. + * + * @return True if property is an enum. + */ public boolean isEnum() { return m_kind == Kind.kEnum; } + /** + * Returns property value. + * + * @return Property value. + */ public int get() { return CameraServerJNI.getProperty(m_handle); } + /** + * Sets property value. + * + * @param value Property value. + */ public void set(int value) { CameraServerJNI.setProperty(m_handle, value); } + /** + * Returns property minimum value. + * + * @return Property minimum value. + */ public int getMin() { return CameraServerJNI.getPropertyMin(m_handle); } + /** + * Returns property maximum value. + * + * @return Property maximum value. + */ public int getMax() { return CameraServerJNI.getPropertyMax(m_handle); } + /** + * Returns property step size. + * + * @return Property step size. + */ public int getStep() { return CameraServerJNI.getPropertyStep(m_handle); } + /** + * Returns property default value. + * + * @return Property default value. + */ public int getDefault() { return CameraServerJNI.getPropertyDefault(m_handle); } - // String-specific functions + /** + * Returns the string property value. + * + *

This function is string-specific. + * + * @return The string property value. + */ public String getString() { return CameraServerJNI.getStringProperty(m_handle); } + /** + * Sets the string property value. + * + *

This function is string-specific. + * + * @param value String property value. + */ public void setString(String value) { CameraServerJNI.setStringProperty(m_handle, value); } - // Enum-specific functions + /** + * Returns the possible values for the enum property value. + * + *

This function is enum-specific. + * + * @return The possible values for the enum property value. + */ public String[] getChoices() { return CameraServerJNI.getEnumPropertyChoices(m_handle); } diff --git a/cscore/src/main/java/edu/wpi/first/cscore/VideoSink.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoSink.java index 8b07f4634f4..661ae9adb3d 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/VideoSink.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoSink.java @@ -9,10 +9,15 @@ * (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; @@ -21,6 +26,11 @@ public enum Kind { this.value = value; } + /** + * Returns the Kind value. + * + * @return The Kind value. + */ public int getValue() { return value; } @@ -43,6 +53,11 @@ public static Kind getKindFromInt(int kind) { } } + /** + * Constructs a VideoSink. + * + * @param handle The video sink handle. + */ protected VideoSink(int handle) { m_handle = handle; } @@ -55,10 +70,20 @@ public synchronized void close() { m_handle = 0; } + /** + * Returns true if the VideoSink is valid. + * + * @return True if the VideoSink is valid. + */ public boolean isValid() { return m_handle != 0; } + /** + * Returns the video sink handle. + * + * @return The video sink handle. + */ public int getHandle() { return m_handle; } @@ -217,5 +242,6 @@ public static VideoSink[] enumerateSinks() { return rv; } + /** The VideoSink handle. */ protected int m_handle; } diff --git a/cscore/src/main/java/edu/wpi/first/cscore/VideoSource.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoSource.java index 32c784998a9..2356940ef3b 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/VideoSource.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoSource.java @@ -11,11 +11,17 @@ * (e.g. from a stereo or depth camera); these are called channels. */ public class VideoSource implements AutoCloseable { + /** Video source kind. */ 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; @@ -24,6 +30,11 @@ public enum Kind { this.value = value; } + /** + * Returns the Kind value. + * + * @return The Kind value. + */ public int getValue() { return value; } @@ -51,6 +62,11 @@ public enum ConnectionStrategy { this.value = value; } + /** + * Returns the ConnectionStrategy value. + * + * @return The ConnectionStrategy value. + */ public int getValue() { return value; } @@ -75,6 +91,11 @@ public static Kind getKindFromInt(int kind) { } } + /** + * Constructs a VideoSource. + * + * @param handle The video source handle. + */ protected VideoSource(int handle) { m_handle = handle; } @@ -87,10 +108,20 @@ public synchronized void close() { m_handle = 0; } + /** + * Returns true if the VideoSource is valid. + * + * @return True if the VideoSource is valid. + */ public boolean isValid() { return m_handle != 0; } + /** + * Returns the video source handle. + * + * @return The video source handle. + */ public int getHandle() { return m_handle; } @@ -374,5 +405,6 @@ public static VideoSource[] enumerateSources() { return rv; } + /** Video source handle. */ protected int m_handle; } diff --git a/cscore/src/main/native/include/cscore_oo.h b/cscore/src/main/native/include/cscore_oo.h index f13a6fd9772..3a55d863767 100644 --- a/cscore/src/main/native/include/cscore_oo.h +++ b/cscore/src/main/native/include/cscore_oo.h @@ -44,42 +44,153 @@ 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 }; VideoProperty() = default; + /** + * Returns property name. + * + * @return Property name. + */ std::string GetName() const; + /** + * Returns property kind. + * + * @return Property kind. + */ Kind GetKind() const { return m_kind; } + /** + * Returns true if property is valid. + * + * @return True if property is valid. + */ explicit operator bool() const { return m_kind != kNone; } - // Kind checkers + /** + * Returns true if property is a boolean. + * + * @return True if property is a boolean. + */ bool IsBoolean() const { return m_kind == kBoolean; } + + /** + * Returns true if property is an integer. + * + * @return True if property is an integer. + */ bool IsInteger() const { return m_kind == kInteger; } + + /** + * Returns true if property is a string. + * + * @return True if property is a string. + */ bool IsString() const { return m_kind == kString; } + + /** + * Returns true if property is an enum. + * + * @return True if property is an enum. + */ bool IsEnum() const { return m_kind == kEnum; } + /** + * Returns property value. + * + * @return Property value. + */ int Get() const; + + /** + * Sets property value. + * + * @param value Property value. + */ void Set(int value); + + /** + * Returns property minimum value. + * + * @return Property minimum value. + */ int GetMin() const; + + /** + * Returns property maximum value. + * + * @return Property maximum value. + */ int GetMax() const; + + /** + * Returns property step size. + * + * @return Property step size. + */ int GetStep() const; + + /** + * Returns property default value. + * + * @return Property default value. + */ int GetDefault() const; - // String-specific functions + /** + * Returns the string property value. + * + *

This function is string-specific. + * + * @return The string property value. + */ std::string GetString() const; + + /** + * Returns the string property value as a reference to the given buffer. + * + * This function is string-specific. + * + * @param buf The backing storage to which to write the property value. + * @return The string property value as a reference to the given buffer. + */ std::string_view GetString(wpi::SmallVectorImpl& buf) const; + + /** + * Sets the string property value. + * + * This function is string-specific. + * + * @param value String property value. + */ void SetString(std::string_view value); - // Enum-specific functions + /** + * Returns the possible values for the enum property value. + * + * This function is enum-specific. + * + * @return The possible values for the enum property value. + */ std::vector GetChoices() const; + /** + * Returns the last status. + * + * @return The last status. + */ CS_Status GetLastStatus() const { return m_status; } private: @@ -99,10 +210,17 @@ class VideoSource { friend class VideoSink; public: + /** + * Video source kind. + */ 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 }; @@ -350,6 +468,8 @@ class VideoSource { explicit VideoSource(CS_Source handle) : m_handle(handle) {} mutable CS_Status m_status = 0; + + /// Video source handle. CS_Source m_handle{0}; }; @@ -358,11 +478,19 @@ class VideoSource { */ class VideoCamera : public VideoSource { public: + /** + * White balance. + */ enum WhiteBalance { + /// Fixed indoor white balance. kFixedIndoor = 3000, + /// Fixed outdoor white balance 1. kFixedOutdoor1 = 4000, + /// Fixed outdoor white balance 2. kFixedOutdoor2 = 5000, + /// Fixed fluorescent white balance 1. kFixedFluorescent1 = 5100, + /// Fixed fluorescent white balance 2. kFixedFlourescent2 = 5200 }; @@ -470,10 +598,17 @@ class UsbCamera : public VideoCamera { */ class HttpCamera : public VideoCamera { public: + /** + * HTTP camera kind. + */ 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 +851,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 }; @@ -727,8 +865,18 @@ class VideoSink { VideoSink& operator=(VideoSink other) noexcept; ~VideoSink(); + /** + * Returns true if the VideoSink is valid. + * + * @return True if the VideoSink is valid. + */ explicit operator bool() const { return m_handle != 0; } + /** + * Returns the VideoSink handle. + * + * @return The VideoSink handle. + */ int GetHandle() const { return m_handle; } bool operator==(const VideoSink& other) const { @@ -972,17 +1120,23 @@ class ImageSink : public VideoSink { class VideoEvent : public RawEvent { public: /** - * Get the source associated with the event (if any). + * Returns the source associated with the event (if any). + * + * @return The source associated with the event (if any). */ VideoSource GetSource() const; /** - * Get the sink associated with the event (if any). + * Returns the sink associated with the event (if any). + * + * @return The sink associated with the event (if any). */ VideoSink GetSink() const; /** - * Get the property associated with the event (if any). + * Returns the property associated with the event (if any). + * + * @return The property associated with the event (if any). */ VideoProperty GetProperty() const; }; diff --git a/datalogtool/publish.gradle b/datalogtool/publish.gradle index f24f51aa482..ae75905e7ee 100644 --- a/datalogtool/publish.gradle +++ b/datalogtool/publish.gradle @@ -29,51 +29,8 @@ model { def applicationPath = binary.executable.file def icon = file("$project.projectDir/src/main/native/mac/datalogtool.icns") - // Create the macOS bundle. - def bundleTask = project.tasks.create("bundleDataLogToolOsxApp" + binary.targetPlatform.architecture.name, Copy) { - description("Creates a macOS application bundle for DataLogTool") - from(file("$project.projectDir/Info.plist")) - into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/DataLogTool.app/Contents")) - into("MacOS") { - with copySpec { - from binary.executable.file - } - } - into("Resources") { - with copySpec { - from icon - } - } - - inputs.property "HasDeveloperId", project.hasProperty("developerID") - - doLast { - if (project.hasProperty("developerID")) { - // Get path to binary. - exec { - workingDir rootDir - def args = [ - "sh", - "-c", - "codesign --force --strict --deep " + - "--timestamp --options=runtime " + - "--verbose -s ${project.findProperty("developerID")} " + - "$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/DataLogTool.app/" - ] - commandLine args - } - } - } - } - - // Reset the application path if we are creating a bundle. - if (binary.targetPlatform.operatingSystem.isMacOsX()) { - applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name") - project.build.dependsOn bundleTask - } - // Create the ZIP. - def task = project.tasks.create("copyDataLogToolExecutable" + binary.targetPlatform.architecture.name, Zip) { + def task = project.tasks.create("copyDataLogToolExecutable" + binary.targetPlatform.operatingSystem.name + binary.targetPlatform.architecture.name, Zip) { description("Copies the DataLogTool executable to the outputs directory.") destinationDirectory = outputsFolder @@ -85,13 +42,63 @@ model { into '/' } - from(applicationPath) + if (binary.targetPlatform.operatingSystem.isWindows()) { + def exePath = binary.executable.file.absolutePath + exePath = exePath.substring(0, exePath.length() - 4) + def pdbPath = new File(exePath + '.pdb') + from(pdbPath) + } + into(nativeUtils.getPlatformPath(binary)) } if (binary.targetPlatform.operatingSystem.isMacOsX()) { + // Create the macOS bundle. + def bundleTask = project.tasks.create("bundleDataLogToolOsxApp" + binary.targetPlatform.architecture.name, Copy) { + description("Creates a macOS application bundle for DataLogTool") + from(file("$project.projectDir/Info.plist")) + into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/DataLogTool.app/Contents")) + into("MacOS") { + with copySpec { + from binary.executable.file + } + } + into("Resources") { + with copySpec { + from icon + } + } + + inputs.property "HasDeveloperId", project.hasProperty("developerID") + + doLast { + if (project.hasProperty("developerID")) { + // Get path to binary. + exec { + workingDir rootDir + def args = [ + "sh", + "-c", + "codesign --force --strict --deep " + + "--timestamp --options=runtime " + + "--verbose -s ${project.findProperty("developerID")} " + + "$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/DataLogTool.app/" + ] + commandLine args + } + } + } + } + + // Reset the application path if we are creating a bundle. + applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name") + task.from(applicationPath) + project.build.dependsOn bundleTask + bundleTask.dependsOn binary.tasks.link task.dependsOn(bundleTask) + } else { + task.from(applicationPath) } task.dependsOn binary.tasks.link diff --git a/datalogtool/src/main/generate/WPILibVersion.cpp.in b/datalogtool/src/main/generate/WPILibVersion.cpp.in index b0a44905207..cfe24411588 100644 --- a/datalogtool/src/main/generate/WPILibVersion.cpp.in +++ b/datalogtool/src/main/generate/WPILibVersion.cpp.in @@ -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. */ diff --git a/datalogtool/src/main/native/cpp/DataLogThread.cpp b/datalogtool/src/main/native/cpp/DataLogThread.cpp deleted file mode 100644 index 90c8b196c62..00000000000 --- a/datalogtool/src/main/native/cpp/DataLogThread.cpp +++ /dev/null @@ -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 - -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; -} diff --git a/datalogtool/src/main/native/cpp/DataLogThread.h b/datalogtool/src/main/native/cpp/DataLogThread.h deleted file mode 100644 index 267aa1fef8b..00000000000 --- a/datalogtool/src/main/native/cpp/DataLogThread.h +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -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 - 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 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 m_numRecords{0}; - std::map> m_entryNames; - wpi::DenseMap m_entries; - std::thread m_thread; -}; diff --git a/datalogtool/src/main/native/cpp/Exporter.cpp b/datalogtool/src/main/native/cpp/Exporter.cpp index 9201c8f7a1d..3c0ade12b89 100644 --- a/datalogtool/src/main/native/cpp/Exporter.cpp +++ b/datalogtool/src/main/native/cpp/Exporter.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -32,11 +33,10 @@ #include #include "App.h" -#include "DataLogThread.h" namespace { struct InputFile { - explicit InputFile(std::unique_ptr datalog); + explicit InputFile(std::unique_ptr 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 datalog; + std::unique_ptr datalog; std::string status; bool highlight = false; }; @@ -135,7 +135,7 @@ static void RebuildEntryTree() { } } -InputFile::InputFile(std::unique_ptr datalog_) +InputFile::InputFile(std::unique_ptr datalog_) : filename{datalog_->GetBufferIdentifier()}, stem{fs::path{filename}.stem().string()}, datalog{std::move(datalog_)} { @@ -192,7 +192,7 @@ static std::unique_ptr LoadDataLog(std::string_view filename) { } return std::make_unique( - std::make_unique(std::move(reader))); + std::make_unique(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(); } diff --git a/datalogtool/src/main/native/cpp/main.cpp b/datalogtool/src/main/native/cpp/main.cpp index 5f1261b00f4..3d1a1965f29 100644 --- a/datalogtool/src/main/native/cpp/main.cpp +++ b/datalogtool/src/main/native/cpp/main.cpp @@ -1,25 +1,25 @@ -// 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 - -void Application(std::string_view saveDir); - -#ifdef _WIN32 -int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* pCmdLine, - int nCmdShow) { - int argc = __argc; - char** argv = __argv; -#else -int main(int argc, char** argv) { -#endif - std::string_view saveDir; - if (argc == 2) { - saveDir = argv[1]; - } - - Application(saveDir); - - return 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 + +void Application(std::string_view saveDir); + +#ifdef _WIN32 +int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* pCmdLine, + int nCmdShow) { + int argc = __argc; + char** argv = __argv; +#else +int main(int argc, char** argv) { +#endif + std::string_view saveDir; + if (argc == 2) { + saveDir = argv[1]; + } + + Application(saveDir); + + return 0; +} diff --git a/docs/build.gradle b/docs/build.gradle index c4be119e389..0d6e87487a9 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -208,21 +208,27 @@ 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:" + + // TODO: v Document these, then remove them from the list + "-edu.wpi.first.hal," + + "-edu.wpi.first.hal.can," + + "-edu.wpi.first.hal.simulation," + + // TODO: ^ Document these, then remove them from the list + "-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) diff --git a/fieldImages/src/main/java/edu/wpi/fields/FieldConfig.java b/fieldImages/src/main/java/edu/wpi/fields/FieldConfig.java index f33ddca6dc7..d8752c64b98 100644 --- a/fieldImages/src/main/java/edu/wpi/fields/FieldConfig.java +++ b/fieldImages/src/main/java/edu/wpi/fields/FieldConfig.java @@ -40,12 +40,10 @@ public static class Corners { public FieldConfig() {} - @SuppressWarnings("deprecation") public URL getImageUrl() { return getClass().getResource(Fields.kBaseResourceDir + m_fieldImage); } - @SuppressWarnings("deprecation") public InputStream getImageAsStream() { return getClass().getResourceAsStream(Fields.kBaseResourceDir + m_fieldImage); } diff --git a/fieldImages/src/main/java/edu/wpi/fields/Fields.java b/fieldImages/src/main/java/edu/wpi/fields/Fields.java index 119415edefc..a92cfb0f5c3 100644 --- a/fieldImages/src/main/java/edu/wpi/fields/Fields.java +++ b/fieldImages/src/main/java/edu/wpi/fields/Fields.java @@ -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; diff --git a/fieldImages/src/main/native/cpp/fields.cpp b/fieldImages/src/main/native/cpp/fields.cpp index 4f79e6bf775..6fd6af09366 100644 --- a/fieldImages/src/main/native/cpp/fields.cpp +++ b/fieldImages/src/main/native/cpp/fields.cpp @@ -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, diff --git a/fieldImages/src/main/native/include/fields/2024-crescendo.h b/fieldImages/src/main/native/include/fields/2024-crescendo.h new file mode 100644 index 00000000000..11947efa5e2 --- /dev/null +++ b/fieldImages/src/main/native/include/fields/2024-crescendo.h @@ -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 + +namespace fields { +std::string_view GetResource_2024_crescendo_json(); +std::string_view GetResource_2024_field_png(); +} // namespace fields diff --git a/fieldImages/src/main/native/resources/edu/wpi/first/fields/2024-crescendo.json b/fieldImages/src/main/native/resources/edu/wpi/first/fields/2024-crescendo.json new file mode 100644 index 00000000000..8a601a2acdd --- /dev/null +++ b/fieldImages/src/main/native/resources/edu/wpi/first/fields/2024-crescendo.json @@ -0,0 +1,19 @@ +{ + "game": "Crescendo", + "field-image": "2024-field.png", + "field-corners": { + "top-left": [ + 150, + 79 + ], + "bottom-right": [ + 2961, + 1476 + ] + }, + "field-size": [ + 54.27083, + 26.9375 + ], + "field-unit": "foot" +} diff --git a/fieldImages/src/main/native/resources/edu/wpi/first/fields/2024-field.png b/fieldImages/src/main/native/resources/edu/wpi/first/fields/2024-field.png new file mode 100644 index 00000000000..a2a7665f728 Binary files /dev/null and b/fieldImages/src/main/native/resources/edu/wpi/first/fields/2024-field.png differ diff --git a/glass/publish.gradle b/glass/publish.gradle index 0797fa15ef6..f7c59716d65 100644 --- a/glass/publish.gradle +++ b/glass/publish.gradle @@ -97,8 +97,6 @@ model { into '/' } - from(applicationPath) - if (binary.targetPlatform.operatingSystem.isWindows()) { def exePath = binary.executable.file.absolutePath exePath = exePath.substring(0, exePath.length() - 4) @@ -149,10 +147,13 @@ model { // Reset the application path if we are creating a bundle. applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name") + task.from(applicationPath) project.build.dependsOn bundleTask bundleTask.dependsOn binary.tasks.link task.dependsOn(bundleTask) + } else { + task.from(applicationPath) } task.dependsOn binary.tasks.link diff --git a/glass/src/app/generate/WPILibVersion.cpp.in b/glass/src/app/generate/WPILibVersion.cpp.in index b0a44905207..cfe24411588 100644 --- a/glass/src/app/generate/WPILibVersion.cpp.in +++ b/glass/src/app/generate/WPILibVersion.cpp.in @@ -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. */ diff --git a/glass/src/app/native/cpp/main.cpp b/glass/src/app/native/cpp/main.cpp index 0b3473c15cd..ec0c2c0ddd6 100644 --- a/glass/src/app/native/cpp/main.cpp +++ b/glass/src/app/native/cpp/main.cpp @@ -96,7 +96,7 @@ static void NtInitialize() { auto inst = nt::GetDefaultInstance(); auto poller = nt::CreateListenerPoller(inst); nt::AddPolledListener(poller, inst, NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE); - nt::AddPolledLogger(poller, 0, 100); + nt::AddPolledLogger(poller, NT_LOG_INFO, 100); gui::AddEarlyExecute([inst, poller] { auto win = gui::GetSystemWindow(); if (!win) { diff --git a/glass/src/lib/native/cpp/other/FMS.cpp b/glass/src/lib/native/cpp/other/FMS.cpp index 67c3f8c904e..d49304e0396 100644 --- a/glass/src/lib/native/cpp/other/FMS.cpp +++ b/glass/src/lib/native/cpp/other/FMS.cpp @@ -14,7 +14,7 @@ using namespace glass; static const char* stations[] = {"Invalid", "Red 1", "Red 2", "Red 3", "Blue 1", "Blue 2", "Blue 3"}; -void glass::DisplayFMS(FMSModel* model) { +void glass::DisplayFMS(FMSModel* model, bool editableDsAttached) { if (!model->Exists() || model->IsReadOnly()) { return DisplayFMSReadOnly(model); } @@ -31,10 +31,17 @@ void glass::DisplayFMS(FMSModel* model) { // DS Attached if (auto data = model->GetDsAttachedData()) { bool val = data->GetValue(); - if (ImGui::Checkbox("DS Attached", &val)) { - model->SetDsAttached(val); + if (editableDsAttached) { + if (ImGui::Checkbox("DS Attached", &val)) { + model->SetDsAttached(val); + } + data->EmitDrag(); + } else { + ImGui::Selectable("DS Attached: "); + data->EmitDrag(); + ImGui::SameLine(); + ImGui::TextUnformatted(val ? "Yes" : "No"); } - data->EmitDrag(); } // Alliance Station diff --git a/glass/src/lib/native/cpp/other/Field2D.cpp b/glass/src/lib/native/cpp/other/Field2D.cpp index a99da09f226..67d021200f2 100644 --- a/glass/src/lib/native/cpp/other/Field2D.cpp +++ b/glass/src/lib/native/cpp/other/Field2D.cpp @@ -222,8 +222,8 @@ class ObjectInfo { class FieldInfo { public: - static constexpr auto kDefaultWidth = 15.98_m; - static constexpr auto kDefaultHeight = 8.21_m; + static constexpr auto kDefaultWidth = 16.541052_m; + static constexpr auto kDefaultHeight = 8.211_m; explicit FieldInfo(Storage& storage); @@ -343,7 +343,7 @@ static bool InputPose(frc::Pose2d* pose) { } FieldInfo::FieldInfo(Storage& storage) - : m_builtin{storage.GetString("builtin")}, + : m_builtin{storage.GetString("builtin", "2024 Crescendo")}, m_filename{storage.GetString("image")}, m_width{storage.GetFloat("width", kDefaultWidth.to())}, m_height{storage.GetFloat("height", kDefaultHeight.to())}, @@ -508,6 +508,16 @@ bool FieldInfo::LoadJson(std::span is, std::string_view filename) { height = units::convert(height); } + // check scaling + int fieldWidth = m_right - m_left; + int fieldHeight = m_bottom - m_top; + if (std::abs((fieldWidth / width) - (fieldHeight / height)) > 0.3) { + fmt::print(stderr, + "GUI: Field X and Y scaling substantially different: " + "xscale={} yscale={}\n", + (fieldWidth / width), (fieldHeight / height)); + } + if (!filename.empty()) { // the image filename is relative to the json file auto pathname = fs::path{filename}.replace_filename(image).string(); @@ -560,23 +570,29 @@ FieldFrameData FieldInfo::GetFrameData(ImVec2 min, ImVec2 max) const { // fit the image into the window if (m_texture && m_imageHeight != 0 && m_imageWidth != 0) { gui::MaxFit(&min, &max, m_imageWidth, m_imageHeight); + } else { + gui::MaxFit(&min, &max, m_width, m_height); } FieldFrameData ffd; ffd.imageMin = min; ffd.imageMax = max; - // size down the box by the image corners (if any) - if (m_bottom > 0 && m_right > 0) { - min.x += m_left * (max.x - min.x) / m_imageWidth; - min.y += m_top * (max.y - min.y) / m_imageHeight; - max.x -= (m_imageWidth - m_right) * (max.x - min.x) / m_imageWidth; - max.y -= (m_imageHeight - m_bottom) * (max.y - min.y) / m_imageHeight; + if (m_bottom > 0 && m_right > 0 && m_imageWidth != 0) { + // size down the box by the image corners + float scale = (max.x - min.x) / m_imageWidth; + min.x += m_left * scale; + min.y += m_top * scale; + max.x -= (m_imageWidth - m_right) * scale; + max.y -= (m_imageHeight - m_bottom) * scale; + } else if ((max.x - min.x) > 40 && (max.y - min.y > 40)) { + // ensure there's some padding + min.x += 20; + max.x -= 20; + min.y += 20; + max.y -= 20; } - // draw the field "active area" as a yellow boundary box - gui::MaxFit(&min, &max, m_width, m_height); - ffd.min = min; ffd.max = max; ffd.scale = (max.x - min.x) / m_width; diff --git a/glass/src/lib/native/cpp/support/DataLogReaderThread.cpp b/glass/src/lib/native/cpp/support/DataLogReaderThread.cpp new file mode 100644 index 00000000000..9d90bb39662 --- /dev/null +++ b/glass/src/lib/native/cpp/support/DataLogReaderThread.cpp @@ -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 + +#include +#include + +using namespace glass; + +DataLogReaderThread::~DataLogReaderThread() { + if (m_thread.joinable()) { + m_active = false; + m_thread.join(); + } +} + +void DataLogReaderThread::ReadMain() { + wpi::SmallDenseMap< + int, std::pair>, 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{}); + } + 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(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; +} diff --git a/glass/src/lib/native/include/glass/other/FMS.h b/glass/src/lib/native/include/glass/other/FMS.h index 5970e93e229..f039c15a702 100644 --- a/glass/src/lib/native/include/glass/other/FMS.h +++ b/glass/src/lib/native/include/glass/other/FMS.h @@ -46,8 +46,9 @@ class FMSModel : public Model { * * @param matchTimeEnabled If not null, a checkbox is displayed for * "enable match time" linked to this value + * @param editableDsAttached If true, DS attached should be editable */ -void DisplayFMS(FMSModel* model); +void DisplayFMS(FMSModel* model, bool editableDsAttached); void DisplayFMSReadOnly(FMSModel* model); } // namespace glass diff --git a/glass/src/lib/native/include/glass/support/DataLogReaderThread.h b/glass/src/lib/native/include/glass/support/DataLogReaderThread.h new file mode 100644 index 00000000000..bcbc9623ef8 --- /dev/null +++ b/glass/src/lib/native/include/glass/support/DataLogReaderThread.h @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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 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 + 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 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 m_numRecords{0}; + std::map> m_entriesByName; + wpi::DenseMap m_entriesById; + wpi::StructDescriptorDatabase m_structDb; + wpi::ProtobufMessageDatabase m_protoDb; + std::thread m_thread; +}; + +} // namespace glass diff --git a/glass/src/libnt/native/cpp/NTStringChooser.cpp b/glass/src/libnt/native/cpp/NTStringChooser.cpp index b892a2c789e..26074fc4814 100644 --- a/glass/src/libnt/native/cpp/NTStringChooser.cpp +++ b/glass/src/libnt/native/cpp/NTStringChooser.cpp @@ -5,6 +5,7 @@ #include "glass/networktables/NTStringChooser.h" #include +#include 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() { diff --git a/glass/src/libnt/native/cpp/NetworkTablesProvider.cpp b/glass/src/libnt/native/cpp/NetworkTablesProvider.cpp index 59cf1fcbed3..cef4b069770 100644 --- a/glass/src/libnt/native/cpp/NetworkTablesProvider.cpp +++ b/glass/src/libnt/native/cpp/NetworkTablesProvider.cpp @@ -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) { diff --git a/glass/src/libnt/native/cpp/StandardNetworkTables.cpp b/glass/src/libnt/native/cpp/StandardNetworkTables.cpp index 9031873ad83..4cc21212690 100644 --- a/glass/src/libnt/native/cpp/StandardNetworkTables.cpp +++ b/glass/src/libnt/native/cpp/StandardNetworkTables.cpp @@ -63,7 +63,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) { [](Window* win, Model* model, const char*) { win->SetFlags(ImGuiWindowFlags_AlwaysAutoResize); return MakeFunctionView( - [=] { DisplayFMS(static_cast(model)); }); + [=] { DisplayFMS(static_cast(model), true); }); }); provider.Register( NTDigitalInputModel::kType, diff --git a/glass/src/libnt/native/include/glass/networktables/NTStringChooser.h b/glass/src/libnt/native/include/glass/networktables/NTStringChooser.h index d770a749e3f..49c4802ede3 100644 --- a/glass/src/libnt/native/include/glass/networktables/NTStringChooser.h +++ b/glass/src/libnt/native/include/glass/networktables/NTStringChooser.h @@ -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; diff --git a/hal/.styleguide b/hal/.styleguide index bdd9bc47c30..c04d378d074 100644 --- a/hal/.styleguide +++ b/hal/.styleguide @@ -20,7 +20,6 @@ generatedFileExclude { modifiableFileExclude { \.patch$ \.png$ - \.py$ \.so$ } diff --git a/hal/generate_usage_reporting.py b/hal/generate_usage_reporting.py index c355ea298f5..08536a86e2c 100755 --- a/hal/generate_usage_reporting.py +++ b/hal/generate_usage_reporting.py @@ -10,40 +10,56 @@ def main(): # Gets the folder this script is in (the hal/ directory) HAL_ROOT = pathlib.Path(__file__).parent java_package = "edu/wpi/first/hal" - (HAL_ROOT/"src/generated/main/native/include/hal").mkdir(parents=True, exist_ok=True) - (HAL_ROOT/f"src/generated/main/java/{java_package}").mkdir(parents=True, exist_ok=True) - usage_reporting_types_cpp = [] + # fmt: off + (HAL_ROOT / "src/generated/main/native/include/hal").mkdir(parents=True, exist_ok=True) + (HAL_ROOT / f"src/generated/main/java/{java_package}").mkdir(parents=True, exist_ok=True) + # fmt: on + usage_reporting_types_cpp = [] usage_reporting_instances_cpp = [] usage_reporting_types = [] usage_reporting_instances = [] - with open(HAL_ROOT/"src/generate/Instances.txt") as instances: + with open(HAL_ROOT / "src/generate/Instances.txt") as instances: for instance in instances: usage_reporting_instances_cpp.append(f" {instance.strip()},") usage_reporting_instances.append( f" /** {instance.strip()}. */\n" - f" public static final int {instance.strip()};") + f" public static final int {instance.strip()};" + ) - with open(HAL_ROOT/"src/generate/ResourceType.txt") as resource_types: + with open(HAL_ROOT / "src/generate/ResourceType.txt") as resource_types: for resource_type in resource_types: usage_reporting_types_cpp.append(f" {resource_type.strip()},") usage_reporting_types.append( f" /** {resource_type.strip()}. */\n" - f" public static final int {resource_type.strip()};") + f" public static final int {resource_type.strip()};" + ) - with open(HAL_ROOT/"src/generate/FRCNetComm.java.in") as java_usage_reporting: - contents = (java_usage_reporting.read() + with open(HAL_ROOT / "src/generate/FRCNetComm.java.in") as java_usage_reporting: + contents = ( + # fmt: off + java_usage_reporting.read() .replace(r"${usage_reporting_types}", "\n".join(usage_reporting_types)) - .replace(r"${usage_reporting_instances}", "\n".join(usage_reporting_instances))) - - with open(HAL_ROOT/f"src/generated/main/java/{java_package}/FRCNetComm.java", "w") as java_out: + .replace(r"${usage_reporting_instances}", "\n".join(usage_reporting_instances)) + # fmt: on + ) + + with open( + HAL_ROOT / f"src/generated/main/java/{java_package}/FRCNetComm.java", "w" + ) as java_out: java_out.write(contents) - with open(HAL_ROOT/"src/generate/FRCUsageReporting.h.in") as cpp_usage_reporting: - contents = (cpp_usage_reporting.read() + with open(HAL_ROOT / "src/generate/FRCUsageReporting.h.in") as cpp_usage_reporting: + contents = ( + # fmt: off + cpp_usage_reporting.read() .replace(r"${usage_reporting_types_cpp}", "\n".join(usage_reporting_types_cpp)) - .replace(r"${usage_reporting_instances_cpp}", "\n".join(usage_reporting_instances_cpp))) + .replace(r"${usage_reporting_instances_cpp}", "\n".join(usage_reporting_instances_cpp)) + # fmt: on + ) - with open(HAL_ROOT/"src/generated/main/native/include/hal/FRCUsageReporting.h", "w") as cpp_out: + with open( + HAL_ROOT / "src/generated/main/native/include/hal/FRCUsageReporting.h", "w" + ) as cpp_out: cpp_out.write(contents) diff --git a/hal/src/generate/FRCNetComm.java.in b/hal/src/generate/FRCNetComm.java.in index 5ca120f92b3..e58a578e816 100644 --- a/hal/src/generate/FRCNetComm.java.in +++ b/hal/src/generate/FRCNetComm.java.in @@ -9,7 +9,7 @@ package edu.wpi.first.hal; /** * JNI wrapper for library FRC_NetworkCommunication
. */ -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() {} } diff --git a/hal/src/generate/Instances.txt b/hal/src/generate/Instances.txt index d136bc0f52e..fc27682eea7 100644 --- a/hal/src/generate/Instances.txt +++ b/hal/src/generate/Instances.txt @@ -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 diff --git a/hal/src/generated/main/java/edu/wpi/first/hal/FRCNetComm.java b/hal/src/generated/main/java/edu/wpi/first/hal/FRCNetComm.java index 318d6518258..0814a3d0fc3 100644 --- a/hal/src/generated/main/java/edu/wpi/first/hal/FRCNetComm.java +++ b/hal/src/generated/main/java/edu/wpi/first/hal/FRCNetComm.java @@ -9,7 +9,7 @@ /** * JNI wrapper for library FRC_NetworkCommunication
. */ -public class FRCNetComm { +public final class FRCNetComm { /** * Resource type from UsageReporting. */ @@ -354,6 +354,8 @@ private tInstances() { 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 @@ private tInstances() { /** kOdometry_SwerveDrive = 3. */ public static final int kOdometry_SwerveDrive = 3; } + + /** Utility class. */ + private FRCNetComm() {} } diff --git a/hal/src/generated/main/native/include/hal/FRCUsageReporting.h b/hal/src/generated/main/native/include/hal/FRCUsageReporting.h index b25a37e21de..024ff13eb4f 100644 --- a/hal/src/generated/main/native/include/hal/FRCUsageReporting.h +++ b/hal/src/generated/main/native/include/hal/FRCUsageReporting.h @@ -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, diff --git a/hal/src/main/java/edu/wpi/first/hal/AccelerometerJNI.java b/hal/src/main/java/edu/wpi/first/hal/AccelerometerJNI.java index 5f34924eb54..fc1bbb588f8 100644 --- a/hal/src/main/java/edu/wpi/first/hal/AccelerometerJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/AccelerometerJNI.java @@ -59,4 +59,7 @@ public class AccelerometerJNI extends JNIWrapper { * @return the Z acceleration */ public static native double getAccelerometerZ(); + + /** Utility class. */ + private AccelerometerJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/AccumulatorResult.java b/hal/src/main/java/edu/wpi/first/hal/AccumulatorResult.java index f416ff8192d..569c94b00ae 100644 --- a/hal/src/main/java/edu/wpi/first/hal/AccumulatorResult.java +++ b/hal/src/main/java/edu/wpi/first/hal/AccumulatorResult.java @@ -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. * diff --git a/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java b/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java index b2695a38274..a7a355237b0 100644 --- a/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java @@ -97,4 +97,7 @@ public static native void setBitTiming( * @see "HAL_StopAddressableLEDOutput" */ public static native void stop(int handle); + + /** Utility class. */ + private AddressableLEDJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/AllianceStationID.java b/hal/src/main/java/edu/wpi/first/hal/AllianceStationID.java index 56c2143c818..6b1fd84ced6 100644 --- a/hal/src/main/java/edu/wpi/first/hal/AllianceStationID.java +++ b/hal/src/main/java/edu/wpi/first/hal/AllianceStationID.java @@ -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 } diff --git a/hal/src/main/java/edu/wpi/first/hal/AnalogGyroJNI.java b/hal/src/main/java/edu/wpi/first/hal/AnalogGyroJNI.java index cbc3d4f8e57..5d483f9c8a9 100644 --- a/hal/src/main/java/edu/wpi/first/hal/AnalogGyroJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/AnalogGyroJNI.java @@ -126,4 +126,7 @@ public static native void setAnalogGyroVoltsPerDegreePerSecond( * @see "HAL_GetAnalogGyroCenter" */ public static native int getAnalogGyroCenter(int handle); + + /** Utility class. */ + private AnalogGyroJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/AnalogJNI.java b/hal/src/main/java/edu/wpi/first/hal/AnalogJNI.java index bf8b27f4927..2884b8c95f0 100644 --- a/hal/src/main/java/edu/wpi/first/hal/AnalogJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/AnalogJNI.java @@ -84,6 +84,16 @@ public interface AnalogTriggerType { */ public static native boolean checkAnalogInputChannel(int channel); + /** + * Checks that the analog output channel number is valid. + * + *

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 interface AnalogTriggerType { */ 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 static native void setAnalogTriggerFiltered( * @see "HAL_GetAnalogTriggerFPGAIndex" */ public static native int getAnalogTriggerFPGAIndex(int analogTriggerHandle); + + /** Utility class. */ + private AnalogJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java b/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java index b42def47ea9..90b16889054 100644 --- a/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java @@ -177,4 +177,7 @@ public static native int writeCANPacketRepeatingNoThrow( */ public static native boolean readCANPacketTimeout( int handle, int apiId, int timeoutMs, CANData data); + + /** Utility class. */ + private CANAPIJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/CANAPITypes.java b/hal/src/main/java/edu/wpi/first/hal/CANAPITypes.java new file mode 100644 index 00000000000..5a8a3e6f86e --- /dev/null +++ b/hal/src/main/java/edu/wpi/first/hal/CANAPITypes.java @@ -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. + * + *

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. + * + *

This enum represents different types of CAN devices. Teams are encouraged to use the + * kMiscellaneous for custom or miscellaneous devices. + * + * @see CAN + * Device Types + */ + 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. + * + *

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 CAN + * Manufacturer IDs + */ + 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() {} +} diff --git a/hal/src/main/java/edu/wpi/first/hal/CANData.java b/hal/src/main/java/edu/wpi/first/hal/CANData.java index 0e2956888cb..ffd2f3919f5 100644 --- a/hal/src/main/java/edu/wpi/first/hal/CANData.java +++ b/hal/src/main/java/edu/wpi/first/hal/CANData.java @@ -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. * diff --git a/hal/src/main/java/edu/wpi/first/hal/CANStreamMessage.java b/hal/src/main/java/edu/wpi/first/hal/CANStreamMessage.java index bdb21128544..5706aa080b8 100644 --- a/hal/src/main/java/edu/wpi/first/hal/CANStreamMessage.java +++ b/hal/src/main/java/edu/wpi/first/hal/CANStreamMessage.java @@ -17,6 +17,9 @@ public class CANStreamMessage { @SuppressWarnings("MemberName") public int messageID; + /** Default constructor. */ + public CANStreamMessage() {} + /** * API used from JNI to set the data. * diff --git a/hal/src/main/java/edu/wpi/first/hal/CTREPCMJNI.java b/hal/src/main/java/edu/wpi/first/hal/CTREPCMJNI.java index b657da592e2..0abe084dd94 100644 --- a/hal/src/main/java/edu/wpi/first/hal/CTREPCMJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/CTREPCMJNI.java @@ -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() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/ConstantsJNI.java b/hal/src/main/java/edu/wpi/first/hal/ConstantsJNI.java index 48434499ffe..2ccef74db35 100644 --- a/hal/src/main/java/edu/wpi/first/hal/ConstantsJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/ConstantsJNI.java @@ -17,4 +17,7 @@ public class ConstantsJNI extends JNIWrapper { * @see "HAL_GetSystemClockTicksPerMicrosecond" */ public static native int getSystemClockTicksPerMicrosecond(); + + /** Utility class. */ + private ConstantsJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/ControlWord.java b/hal/src/main/java/edu/wpi/first/hal/ControlWord.java index 7dff0f46359..31f8a7d0067 100644 --- a/hal/src/main/java/edu/wpi/first/hal/ControlWord.java +++ b/hal/src/main/java/edu/wpi/first/hal/ControlWord.java @@ -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, diff --git a/hal/src/main/java/edu/wpi/first/hal/CounterJNI.java b/hal/src/main/java/edu/wpi/first/hal/CounterJNI.java index ebc4cffe265..6823185137c 100644 --- a/hal/src/main/java/edu/wpi/first/hal/CounterJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/CounterJNI.java @@ -279,4 +279,7 @@ public static native void setCounterDownSourceEdge( * @see "HAL_SetCounterReverseDirection" */ public static native void setCounterReverseDirection(int counterHandle, boolean reverseDirection); + + /** Utility class. */ + private CounterJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/DIOJNI.java b/hal/src/main/java/edu/wpi/first/hal/DIOJNI.java index 3330f4ad3c5..4d597cccddc 100644 --- a/hal/src/main/java/edu/wpi/first/hal/DIOJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/DIOJNI.java @@ -178,4 +178,7 @@ public class DIOJNI extends JNIWrapper { * @see "HAL_SetDigitalPWMOutputChannel" */ public static native void setDigitalPWMOutputChannel(int pwmGenerator, int channel); + + /** Utility class. */ + DIOJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/DMAJNI.java b/hal/src/main/java/edu/wpi/first/hal/DMAJNI.java index 00000d2f2b9..77eebf014ff 100644 --- a/hal/src/main/java/edu/wpi/first/hal/DMAJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/DMAJNI.java @@ -245,4 +245,7 @@ public static native long readDMA( * @return The DMA sample */ public static native DMAJNISample.BaseStore getSensorReadData(int handle); + + /** Utility class. */ + private DMAJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/DMAJNISample.java b/hal/src/main/java/edu/wpi/first/hal/DMAJNISample.java index 7c496a17677..df66c558b0c 100644 --- a/hal/src/main/java/edu/wpi/first/hal/DMAJNISample.java +++ b/hal/src/main/java/edu/wpi/first/hal/DMAJNISample.java @@ -26,6 +26,9 @@ static class BaseStore { private long m_timeStamp; private Map m_propertyMap = new HashMap<>(); + /** Default constructor. */ + public DMAJNISample() {} + public int update(int dmaHandle, double timeoutSeconds) { m_timeStamp = DMAJNI.readDMA(dmaHandle, timeoutSeconds, m_dataBuffer, m_storage); return m_storage[25]; diff --git a/hal/src/main/java/edu/wpi/first/hal/DigitalGlitchFilterJNI.java b/hal/src/main/java/edu/wpi/first/hal/DigitalGlitchFilterJNI.java index 9a16984d89b..ececa130c55 100644 --- a/hal/src/main/java/edu/wpi/first/hal/DigitalGlitchFilterJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/DigitalGlitchFilterJNI.java @@ -59,4 +59,7 @@ public class DigitalGlitchFilterJNI extends JNIWrapper { * @see "HAL_GetFilterPeriod" */ public static native int getFilterPeriod(int filterIndex); + + /** Utility class. */ + private DigitalGlitchFilterJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/DriverStationJNI.java b/hal/src/main/java/edu/wpi/first/hal/DriverStationJNI.java index 4a46b525e16..a7ec9f53431 100644 --- a/hal/src/main/java/edu/wpi/first/hal/DriverStationJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/DriverStationJNI.java @@ -372,5 +372,6 @@ public static native int sendError( */ public static native boolean getOutputsActive(); + /** Utility class. */ private DriverStationJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/DutyCycleJNI.java b/hal/src/main/java/edu/wpi/first/hal/DutyCycleJNI.java index 1e7dfa0ccc6..6920b2970fb 100644 --- a/hal/src/main/java/edu/wpi/first/hal/DutyCycleJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/DutyCycleJNI.java @@ -78,4 +78,7 @@ public class DutyCycleJNI extends JNIWrapper { * @see "HAL_GetDutyCycleFPGAIndex" */ public static native int getFPGAIndex(int handle); + + /** Utility class. */ + private DutyCycleJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/EncoderJNI.java b/hal/src/main/java/edu/wpi/first/hal/EncoderJNI.java index d219db056bd..59503f8c43e 100644 --- a/hal/src/main/java/edu/wpi/first/hal/EncoderJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/EncoderJNI.java @@ -275,4 +275,7 @@ public static native void setEncoderIndexSource( * @see "HAL_GetEncoderEncodingType" */ public static native int getEncoderEncodingType(int encoderHandle); + + /** Utility class. */ + private EncoderJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/HALUtil.java b/hal/src/main/java/edu/wpi/first/hal/HALUtil.java index 11d9b4d74a8..0cadf3914cc 100644 --- a/hal/src/main/java/edu/wpi/first/hal/HALUtil.java +++ b/hal/src/main/java/edu/wpi/first/hal/HALUtil.java @@ -90,6 +90,10 @@ public final class HALUtil extends JNIWrapper { /** * Gets the state of the "USER" button on the roboRIO. * + *

Warning: the User Button is used to stop user programs from automatically loading if it is + * held for more then 5 seconds. Because of this, it's not recommended to be used by teams for any + * other purpose. + * * @return true if the button is currently pressed down * @see "HAL_GetFPGAButton" */ diff --git a/hal/src/main/java/edu/wpi/first/hal/I2CJNI.java b/hal/src/main/java/edu/wpi/first/hal/I2CJNI.java index 90e8ddb4d8f..1b729af805c 100644 --- a/hal/src/main/java/edu/wpi/first/hal/I2CJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/I2CJNI.java @@ -135,4 +135,7 @@ public static native int i2CRead( * @see "HAL_CloseI2C" */ public static native void i2CClose(int port); + + /** Utility class. */ + private I2CJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/InterruptJNI.java b/hal/src/main/java/edu/wpi/first/hal/InterruptJNI.java index c60543566bd..ec60b839236 100644 --- a/hal/src/main/java/edu/wpi/first/hal/InterruptJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/InterruptJNI.java @@ -113,4 +113,7 @@ public static native void setInterruptUpSourceEdge( * @see "HAL_ReleaseWaitingInterrupt" */ public static native void releaseWaitingInterrupt(int interruptHandle); + + /** Utility class. */ + private InterruptJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/JNIWrapper.java b/hal/src/main/java/edu/wpi/first/hal/JNIWrapper.java index 6826cd80743..49bcb4a5ec8 100644 --- a/hal/src/main/java/edu/wpi/first/hal/JNIWrapper.java +++ b/hal/src/main/java/edu/wpi/first/hal/JNIWrapper.java @@ -13,16 +13,30 @@ public class JNIWrapper { static boolean libraryLoaded = false; static RuntimeLoader 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 { @@ -57,4 +71,7 @@ public static synchronized void forceLoad() throws IOException { } public static void suppressUnused(Object object) {} + + /** Utility class. */ + public JNIWrapper() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/LEDJNI.java b/hal/src/main/java/edu/wpi/first/hal/LEDJNI.java index bdb4b02d39f..397328eac47 100644 --- a/hal/src/main/java/edu/wpi/first/hal/LEDJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/LEDJNI.java @@ -25,4 +25,7 @@ public class LEDJNI extends JNIWrapper { * @see "HAL_GetRadioLEDState" */ public static native int getRadioLEDState(); + + /** Utility class. */ + private LEDJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/MatchInfoData.java b/hal/src/main/java/edu/wpi/first/hal/MatchInfoData.java index 699ecc59fd3..d9e9796a343 100644 --- a/hal/src/main/java/edu/wpi/first/hal/MatchInfoData.java +++ b/hal/src/main/java/edu/wpi/first/hal/MatchInfoData.java @@ -22,6 +22,9 @@ public class MatchInfoData { /** Stores the match type. */ public int matchType; + /** Default constructor. */ + public MatchInfoData() {} + /** * Called from JNI to set the structure data. * diff --git a/hal/src/main/java/edu/wpi/first/hal/NotifierJNI.java b/hal/src/main/java/edu/wpi/first/hal/NotifierJNI.java index 5620f032dee..04784bcf4d5 100644 --- a/hal/src/main/java/edu/wpi/first/hal/NotifierJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/NotifierJNI.java @@ -101,4 +101,7 @@ public class NotifierJNI extends JNIWrapper { * @return the FPGA time the notifier returned */ public static native long waitForNotifierAlarm(int notifierHandle); + + /** Utility class. */ + private NotifierJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/PWMJNI.java b/hal/src/main/java/edu/wpi/first/hal/PWMJNI.java index dc2abc10768..3f0e3d4be68 100644 --- a/hal/src/main/java/edu/wpi/first/hal/PWMJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/PWMJNI.java @@ -5,12 +5,41 @@ package edu.wpi.first.hal; public class PWMJNI extends DIOJNI { + /** + * Initializes a PWM port. + * + * @param halPortHandle the port to initialize + * @return the created pwm handle + */ public static native int initializePWMPort(int halPortHandle); + /** + * Checks if a pwm channel is valid. + * + * @param channel the channel to check + * @return true if the channel is valid, otherwise false + */ public static native boolean checkPWMChannel(int channel); + /** + * Frees a PWM port. + * + * @param pwmPortHandle the pwm handle + */ public static native void freePWMPort(int pwmPortHandle); + /** + * Sets the configuration settings for the PWM channel. + * + *

All values are in microseconds. + * + * @param pwmPortHandle the PWM handle + * @param maxPwm the maximum PWM value + * @param deadbandMaxPwm the high range of the center deadband + * @param centerPwm the center PWM value + * @param deadbandMinPwm the low range of the center deadband + * @param minPwm the minimum PWM value + */ public static native void setPWMConfigMicroseconds( int pwmPortHandle, int maxPwm, @@ -19,29 +48,122 @@ public static native void setPWMConfigMicroseconds( int deadbandMinPwm, int minPwm); + /** + * Gets the pwm configuration settings for the PWM channel. + * + *

Values are in microseconds. + * + * @param pwmPortHandle the PWM handle + * @return the pwm configuration settings + */ public static native PWMConfigDataResult getPWMConfigMicroseconds(int pwmPortHandle); + /** + * Sets if the FPGA should output the center value if the input value is within the deadband. + * + * @param pwmPortHandle the PWM handle + * @param eliminateDeadband true to eliminate deadband, otherwise false + */ public static native void setPWMEliminateDeadband(int pwmPortHandle, boolean eliminateDeadband); + /** + * Gets the current eliminate deadband value. + * + * @param pwmPortHandle the PWM handle + * @return true if set, otherwise false + */ public static native boolean getPWMEliminateDeadband(int pwmPortHandle); + /** + * Sets a PWM channel to the desired pulse width in microseconds. + * + * @param pwmPortHandle the PWM handle + * @param microsecondPulseTime the PWM value to set + */ public static native void setPulseTimeMicroseconds(int pwmPortHandle, int microsecondPulseTime); + /** + * Sets a PWM channel to the desired scaled value. + * + *

The values range from -1 to 1 and the period is controlled by the PWM Period and MinHigh + * registers. + * + * @param pwmPortHandle the PWM handle + * @param speed the scaled PWM value to set + */ public static native void setPWMSpeed(int pwmPortHandle, double speed); + /** + * Sets a PWM channel to the desired position value. + * + *

The values range from 0 to 1 and the period is controlled by the PWM Period and MinHigh + * registers. + * + * @param pwmPortHandle the PWM handle + * @param position the positional PWM value to set + */ public static native void setPWMPosition(int pwmPortHandle, double position); + /** + * Gets the current microsecond pulse time from a PWM channel. + * + * @param pwmPortHandle the PWM handle + * @return the current PWM microsecond pulse time + */ public static native int getPulseTimeMicroseconds(int pwmPortHandle); + /** + * Gets a scaled value from a PWM channel. + * + *

The values range from -1 to 1. + * + * @param pwmPortHandle the PWM handle + * @return the current speed PWM value + */ public static native double getPWMSpeed(int pwmPortHandle); + /** + * Gets a position value from a PWM channel. + * + *

The values range from 0 to 1. + * + * @param pwmPortHandle the PWM handle + * @return the current positional PWM value + */ public static native double getPWMPosition(int pwmPortHandle); + /** + * Sets a PWM channel to be disabled. + * + *

The channel is disabled until the next time it is set. Note this is different from just + * setting a 0 speed, as this will actively stop all signaling on the channel. + * + * @param pwmPortHandle the PWM handle. + */ public static native void setPWMDisabled(int pwmPortHandle); + /** + * Forces a PWM signal to go to 0 temporarily. + * + * @param pwmPortHandle the PWM handle. + */ public static native void latchPWMZero(int pwmPortHandle); + /** + * Sets the PWM output to be a continuous high signal while enabled. + * + * @param pwmPortHandle the PWM handle. + */ public static native void setAlwaysHighMode(int pwmPortHandle); + /** + * Sets how how often the PWM signal is squelched, thus scaling the period. + * + * @param pwmPortHandle the PWM handle. + * @param squelchMask the 2-bit mask of outputs to squelch + */ public static native void setPWMPeriodScale(int pwmPortHandle, int squelchMask); + + /** Utility class. */ + private PWMJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/PortsJNI.java b/hal/src/main/java/edu/wpi/first/hal/PortsJNI.java index 20f9b42110d..b47697b0e62 100644 --- a/hal/src/main/java/edu/wpi/first/hal/PortsJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/PortsJNI.java @@ -185,4 +185,7 @@ public class PortsJNI extends JNIWrapper { * @see "HAL_GetNumREVPHChannels" */ public static native int getNumREVPHChannels(); + + /** Utility class. */ + private PortsJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/PowerDistributionJNI.java b/hal/src/main/java/edu/wpi/first/hal/PowerDistributionJNI.java index bc7060d2b4b..93f9a3dccac 100644 --- a/hal/src/main/java/edu/wpi/first/hal/PowerDistributionJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/PowerDistributionJNI.java @@ -282,4 +282,7 @@ public static PowerDistributionStickyFaults getStickyFaults(int handle) { * @see "HAL_GetPowerDistributionVersion" */ public static native PowerDistributionVersion getVersion(int handle); + + /** Utility class. */ + private PowerDistributionJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/PowerDistributionVersion.java b/hal/src/main/java/edu/wpi/first/hal/PowerDistributionVersion.java index fdd1233b25a..bfb5ff2d0e3 100644 --- a/hal/src/main/java/edu/wpi/first/hal/PowerDistributionVersion.java +++ b/hal/src/main/java/edu/wpi/first/hal/PowerDistributionVersion.java @@ -4,18 +4,25 @@ package edu.wpi.first.hal; +/** Power distribution version. */ @SuppressWarnings("MemberName") public class PowerDistributionVersion { + /** Firmware major version number. */ public final int firmwareMajor; + /** Firmware minor version number. */ public final int firmwareMinor; + /** Firmware fix version number. */ public final int firmwareFix; + /** Hardware minor version number. */ public final int hardwareMinor; + /** Hardware major version number. */ public final int hardwareMajor; + /** Unique ID. */ public final int uniqueId; /** diff --git a/hal/src/main/java/edu/wpi/first/hal/PowerJNI.java b/hal/src/main/java/edu/wpi/first/hal/PowerJNI.java index 8dd9548cca5..900b423f344 100644 --- a/hal/src/main/java/edu/wpi/first/hal/PowerJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/PowerJNI.java @@ -167,4 +167,7 @@ public class PowerJNI extends JNIWrapper { * @return current CPU temperature in degrees Celsius */ public static native double getCPUTemp(); + + /** Utility class. */ + private PowerJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java b/hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java index b1935b2f983..1180b9b107c 100644 --- a/hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java @@ -286,4 +286,7 @@ public static REVPHFaults getFaults(int handle) { * @see "HAL_GetREVPHVersion" */ public static native REVPHVersion getVersion(int handle); + + /** Utility class. */ + private REVPHJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/RelayJNI.java b/hal/src/main/java/edu/wpi/first/hal/RelayJNI.java index 10a39b4884e..abe146e1a6c 100644 --- a/hal/src/main/java/edu/wpi/first/hal/RelayJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/RelayJNI.java @@ -57,4 +57,7 @@ public class RelayJNI extends DIOJNI { * @see "HAL_GetRelay" */ public static native boolean getRelay(int relayPortHandle); + + /** Utility class. */ + private RelayJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/SPIJNI.java b/hal/src/main/java/edu/wpi/first/hal/SPIJNI.java index 82b9fed1602..18a0a8e7672 100644 --- a/hal/src/main/java/edu/wpi/first/hal/SPIJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/SPIJNI.java @@ -302,4 +302,7 @@ public static native int spiReadAutoReceivedData( */ public static native void spiConfigureAutoStall( int port, int csToSclkTicks, int stallTicks, int pow2BytesPerRead); + + /** Utility class. */ + private SPIJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/SerialPortJNI.java b/hal/src/main/java/edu/wpi/first/hal/SerialPortJNI.java index e156f3a0a67..052218fbe79 100644 --- a/hal/src/main/java/edu/wpi/first/hal/SerialPortJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/SerialPortJNI.java @@ -205,4 +205,7 @@ public class SerialPortJNI extends JNIWrapper { * @see "HAL_CloseSerial" */ public static native void serialClose(int handle); + + /** Utility class. */ + private SerialPortJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/SimDevice.java b/hal/src/main/java/edu/wpi/first/hal/SimDevice.java index 21ac69aaaaf..4c57d05595e 100644 --- a/hal/src/main/java/edu/wpi/first/hal/SimDevice.java +++ b/hal/src/main/java/edu/wpi/first/hal/SimDevice.java @@ -16,9 +16,13 @@ * edu.wpi.first.wpilibj.ADXRS450_Gyro} for an example implementation. */ public class SimDevice implements AutoCloseable { + /** Sim device direction. */ public enum Direction { + /** Input direction for simulation devices. */ kInput(SimDeviceJNI.kInput), + /** Output direction for simulation devices. */ kOutput(SimDeviceJNI.kOutput), + /** Bidirectional direction for simulation devices. */ kBidir(SimDeviceJNI.kBidir); public final int m_value; diff --git a/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java b/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java index 4352a2df481..fdf09b4435b 100644 --- a/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java @@ -286,4 +286,7 @@ public static void setSimValueBoolean(int handle, boolean value) { * @param handle simulated value handle */ public static native void resetSimValue(int handle); + + /** Utility class. */ + private SimDeviceJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/ThreadsJNI.java b/hal/src/main/java/edu/wpi/first/hal/ThreadsJNI.java index 2ccc8085603..2183ae3d557 100644 --- a/hal/src/main/java/edu/wpi/first/hal/ThreadsJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/ThreadsJNI.java @@ -37,4 +37,7 @@ public class ThreadsJNI extends JNIWrapper { * @see "HAL_SetCurrentThreadPriority" */ public static native boolean setCurrentThreadPriority(boolean realTime, int priority); + + /** Utility class. */ + private ThreadsJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/can/CANExceptionFactory.java b/hal/src/main/java/edu/wpi/first/hal/can/CANExceptionFactory.java index d8fe36e230c..a4ad1e2af0f 100644 --- a/hal/src/main/java/edu/wpi/first/hal/can/CANExceptionFactory.java +++ b/hal/src/main/java/edu/wpi/first/hal/can/CANExceptionFactory.java @@ -7,6 +7,9 @@ import edu.wpi.first.hal.communication.NIRioStatus; import edu.wpi.first.hal.util.UncleanStatusException; +/** + * Checks the status of a CAN message and throws an exception of the appropriate type if necessary. + */ public final class CANExceptionFactory { // FRC Error codes static final int ERR_CANSessionMux_InvalidBuffer = -44086; diff --git a/hal/src/main/java/edu/wpi/first/hal/can/CANJNI.java b/hal/src/main/java/edu/wpi/first/hal/can/CANJNI.java index 177833bf821..ba9ac791ed7 100644 --- a/hal/src/main/java/edu/wpi/first/hal/can/CANJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/can/CANJNI.java @@ -9,27 +9,90 @@ import java.nio.ByteBuffer; import java.nio.IntBuffer; +/** + * CAN API HAL JNI Functions. + * + * @see "hal/CAN.h" + */ @SuppressWarnings("MethodName") public class CANJNI extends JNIWrapper { + /** Flag for sending a CAN message once. */ public static final int CAN_SEND_PERIOD_NO_REPEAT = 0; + + /** Flag for stopping periodic CAN message sends. */ public static final int CAN_SEND_PERIOD_STOP_REPEATING = -1; - /* Flags in the upper bits of the messageID */ + /** Mask for "is frame remote" in message ID. */ public static final int CAN_IS_FRAME_REMOTE = 0x80000000; + + /** Mask for "is frame 11 bits" in message ID. */ public static final int CAN_IS_FRAME_11BIT = 0x40000000; + /** Default constructor. */ + public CANJNI() {} + + /** + * Sends a CAN message. + * + * @param messageID The ID of the CAN message. + * @param data The data bytes to be sent. + * @param periodMs The period in milliseconds at which to send the message, use {@link + * #CAN_SEND_PERIOD_NO_REPEAT} for a single send. + */ public static native void FRCNetCommCANSessionMuxSendMessage( int messageID, byte[] data, int periodMs); + /** + * Receives a CAN message. + * + * @param messageID store for the received message ID (output parameter). + * @param messageIDMask the message ID mask to look for + * @param timeStamp the packet received timestamp (based off of CLOCK_MONOTONIC) (output + * parameter). + * @return The data bytes of the received message. + */ public static native byte[] FRCNetCommCANSessionMuxReceiveMessage( IntBuffer messageID, int messageIDMask, ByteBuffer timeStamp); + /** + * Retrieves the current status of the CAN bus. + * + * @param status The CANStatus object to hold the retrieved status. + */ public static native void getCANStatus(CANStatus status); + /** + * Opens a new CAN stream session for receiving CAN messages with specified filters. + * + * @param messageID The CAN messageID to match against. The bits of the messageID are bitwise + * ANDed with the messageIDMask. + * @param messageIDMask The CAN messageIDMask is a bit-wise mask of bits in the messageID to match + * against. This allows matching against multiple frames. For example, providing an messageID + * of 0x2050001 and a mask of 0x1FFF003F would match all REV motor controller frames for a + * device with CAN ID 1. Providing a mask of 0x1FFFFFFF means that only the exact messageID + * will be matched. Providing a mask of 0 would match any frame of any type. + * @param maxMessages The maximum number of messages that can be buffered in the session. + * @return The handle to the opened CAN stream session. + */ public static native int openCANStreamSession(int messageID, int messageIDMask, int maxMessages); + /** + * Closes a CAN stream session. + * + * @param sessionHandle The handle of the CAN stream session to be closed. + */ public static native void closeCANStreamSession(int sessionHandle); + /** + * Reads messages from a CAN stream session. + * + * @param sessionHandle The handle of the CAN stream session. + * @param messages An array to hold the CANStreamMessage objects (output parameter). + * @param messagesToRead The number of messages to read from the session. + * @return The number of messages read into the buffer + * @throws CANStreamOverflowException If the number of messages to read exceeds the capacity of + * the provided messages array. + */ public static native int readCANStreamSession( int sessionHandle, CANStreamMessage[] messages, int messagesToRead) throws CANStreamOverflowException; diff --git a/hal/src/main/java/edu/wpi/first/hal/can/CANStatus.java b/hal/src/main/java/edu/wpi/first/hal/can/CANStatus.java index 8ec89f74a2a..1966a17c83a 100644 --- a/hal/src/main/java/edu/wpi/first/hal/can/CANStatus.java +++ b/hal/src/main/java/edu/wpi/first/hal/can/CANStatus.java @@ -22,6 +22,9 @@ public class CANStatus { /** The CAN Bus transmit error count. */ public int transmitErrorCount; + /** Default constructor. */ + public CANStatus() {} + /** * Set CAN bus status. * diff --git a/hal/src/main/java/edu/wpi/first/hal/can/CANStreamOverflowException.java b/hal/src/main/java/edu/wpi/first/hal/can/CANStreamOverflowException.java index 41d2bb56a2d..f5e2f21358c 100644 --- a/hal/src/main/java/edu/wpi/first/hal/can/CANStreamOverflowException.java +++ b/hal/src/main/java/edu/wpi/first/hal/can/CANStreamOverflowException.java @@ -8,7 +8,10 @@ import java.io.IOException; public class CANStreamOverflowException extends IOException { + /** The messages. */ private final CANStreamMessage[] m_messages; + + /** The length of messages read. */ private final int m_messagesRead; /** diff --git a/hal/src/main/java/edu/wpi/first/hal/communication/NIRioStatus.java b/hal/src/main/java/edu/wpi/first/hal/communication/NIRioStatus.java index ad17a3e911c..f689b600c45 100644 --- a/hal/src/main/java/edu/wpi/first/hal/communication/NIRioStatus.java +++ b/hal/src/main/java/edu/wpi/first/hal/communication/NIRioStatus.java @@ -4,12 +4,26 @@ package edu.wpi.first.hal.communication; +/** NI RIO status. */ public class NIRioStatus { + /** RIO status offset. */ public static final int kRioStatusOffset = -63000; + /** Success. */ public static final int kRioStatusSuccess = 0; + + /** Buffer invalid size. */ public static final int kRIOStatusBufferInvalidSize = kRioStatusOffset - 80; + + /** Operation timed out. */ public static final int kRIOStatusOperationTimedOut = -52007; + + /** Feature not supported. */ public static final int kRIOStatusFeatureNotSupported = kRioStatusOffset - 193; + + /** Resource not initialized. */ public static final int kRIOStatusResourceNotInitialized = -52010; + + /** Default constructor. */ + public NIRioStatus() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/AccelerometerDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/AccelerometerDataJNI.java index 5ea3726eb75..26975c08404 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/AccelerometerDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/AccelerometerDataJNI.java @@ -6,7 +6,16 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for accelerometer data. */ public class AccelerometerDataJNI extends JNIWrapper { + /** + * Register a callback to be run when this accelerometer activates. + * + * @param index the index + * @param callback the callback + * @param initialNotify whether to run the callback with the initial state + * @return the CallbackStore object associated with this callback + */ public static native int registerActiveCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -53,4 +62,7 @@ public static native int registerZCallback( public static native void setZ(int index, double z); public static native void resetData(int index); + + /** Utility class. */ + private AccelerometerDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/AddressableLEDDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/AddressableLEDDataJNI.java index 8ba0e94c7b5..b1465352cb8 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/AddressableLEDDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/AddressableLEDDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for addressable LED data. */ public class AddressableLEDDataJNI extends JNIWrapper { public static native int registerInitializedCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -54,4 +55,7 @@ public static native int registerRunningCallback( public static native void resetData(int index); public static native int findForChannel(int channel); + + /** Utility class. */ + private AddressableLEDDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogGyroDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogGyroDataJNI.java index a6acc9aa0da..b369dcda15a 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogGyroDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogGyroDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for analog gyro data. */ public class AnalogGyroDataJNI extends JNIWrapper { public static native int registerAngleCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -35,4 +36,7 @@ public static native int registerInitializedCallback( public static native void setInitialized(int index, boolean initialized); public static native void resetData(int index); + + /** Utility class. */ + private AnalogGyroDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogInDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogInDataJNI.java index 4229c09574d..b3dde0e176a 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogInDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogInDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for analog input data. */ public class AnalogInDataJNI extends JNIWrapper { public static native int registerInitializedCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -89,4 +90,7 @@ public static native int registerAccumulatorDeadbandCallback( public static native void setAccumulatorDeadband(int index, int AccumulatorDeadband); public static native void resetData(int index); + + /** Utility class. */ + private AnalogInDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogOutDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogOutDataJNI.java index 6af16a0957c..f8e4eeccd1e 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogOutDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogOutDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for analog output data. */ public class AnalogOutDataJNI extends JNIWrapper { public static native int registerVoltageCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -26,4 +27,7 @@ public static native int registerInitializedCallback( public static native void setInitialized(int index, boolean initialized); public static native void resetData(int index); + + /** Utility class. */ + private AnalogOutDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogTriggerDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogTriggerDataJNI.java index 67b65b50c93..681e5bd8c7a 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogTriggerDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/AnalogTriggerDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for analog trigger data. */ public class AnalogTriggerDataJNI extends JNIWrapper { public static native int registerInitializedCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -37,4 +38,7 @@ public static native int registerTriggerUpperBoundCallback( public static native void resetData(int index); public static native int findForChannel(int channel); + + /** Utility class. */ + private AnalogTriggerDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/BufferCallback.java b/hal/src/main/java/edu/wpi/first/hal/simulation/BufferCallback.java index e93a92127da..7c66563a323 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/BufferCallback.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/BufferCallback.java @@ -4,6 +4,14 @@ package edu.wpi.first.hal.simulation; +/** Interface for simulation buffer callbacks. */ public interface BufferCallback { + /** + * Simulation buffer callback function. + * + * @param name Buffer name. + * @param buffer Buffer. + * @param count Buffer size. + */ void callback(String name, byte[] buffer, int count); } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/CTREPCMDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/CTREPCMDataJNI.java index 6d94438d999..265edd036bd 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/CTREPCMDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/CTREPCMDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for CTRE PCM data. */ public class CTREPCMDataJNI extends JNIWrapper { public static native int registerInitializedCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -68,4 +69,7 @@ public static native void registerAllSolenoidCallbacks( int index, int channel, NotifyCallback callback, boolean initialNotify); public static native void resetData(int index); + + /** Utility class. */ + private CTREPCMDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/DIODataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/DIODataJNI.java index 0c59415ee1e..dbbab99a510 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/DIODataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/DIODataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for DIO data. */ public class DIODataJNI extends JNIWrapper { public static native int registerInitializedCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -53,4 +54,7 @@ public static native int registerFilterIndexCallback( public static native void setFilterIndex(int index, int filterIndex); public static native void resetData(int index); + + /** Utility class. */ + private DIODataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/DigitalPWMDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/DigitalPWMDataJNI.java index addcffcd126..e72a2337b9d 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/DigitalPWMDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/DigitalPWMDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for digital PWM data. */ public class DigitalPWMDataJNI extends JNIWrapper { public static native int registerInitializedCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -37,4 +38,7 @@ public static native int registerPinCallback( public static native void resetData(int index); public static native int findForChannel(int channel); + + /** Utility class. */ + private DigitalPWMDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/DriverStationDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/DriverStationDataJNI.java index 196f0174aea..e310b14d797 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/DriverStationDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/DriverStationDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for Driver Station data. */ public class DriverStationDataJNI extends JNIWrapper { public static native int registerEnabledCallback(NotifyCallback callback, boolean initialNotify); @@ -134,4 +135,7 @@ public static native void setMatchInfo( public static native void setReplayNumber(int replayNumber); public static native void resetData(); + + /** Utility class. */ + private DriverStationDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/DutyCycleDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/DutyCycleDataJNI.java index 8f244cac455..e2ed8735827 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/DutyCycleDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/DutyCycleDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for duty cycle data. */ public class DutyCycleDataJNI extends JNIWrapper { public static native int registerInitializedCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -37,4 +38,7 @@ public static native int registerOutputCallback( public static native void resetData(int index); public static native int findForChannel(int channel); + + /** Utility class. */ + private DutyCycleDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/EncoderDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/EncoderDataJNI.java index e48607a8c15..d853d00508a 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/EncoderDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/EncoderDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for encoder data. */ public class EncoderDataJNI extends JNIWrapper { public static native int registerInitializedCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -99,4 +100,7 @@ public static native int registerDistancePerPulseCallback( public static native void resetData(int index); public static native int findForChannel(int channel); + + /** Utility class. */ + private EncoderDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/I2CDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/I2CDataJNI.java index ebe15ebb8e2..f74d63e074c 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/I2CDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/I2CDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for I2C data. */ public class I2CDataJNI extends JNIWrapper { public static native int registerInitializedCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -25,4 +26,7 @@ public static native int registerInitializedCallback( public static native void cancelWriteCallback(int index, int uid); public static native void resetData(int index); + + /** Utility class. */ + private I2CDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/NotifierDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/NotifierDataJNI.java index 9c7b0a271e7..9323d1afa01 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/NotifierDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/NotifierDataJNI.java @@ -6,8 +6,12 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for notifier data. */ public class NotifierDataJNI extends JNIWrapper { public static native long getNextTimeout(); public static native int getNumNotifiers(); + + /** Utility class. */ + private NotifierDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/PWMDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/PWMDataJNI.java index d66256bbc8e..10f8b6669a7 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/PWMDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/PWMDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for PWM data. */ public class PWMDataJNI extends JNIWrapper { public static native int registerInitializedCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -62,4 +63,7 @@ public static native int registerZeroLatchCallback( public static native void setZeroLatch(int index, boolean zeroLatch); public static native void resetData(int index); + + /** Utility class. */ + private PWMDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/PowerDistributionDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/PowerDistributionDataJNI.java index c17da137a19..80a7e46b1e9 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/PowerDistributionDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/PowerDistributionDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for power distribution data. */ public class PowerDistributionDataJNI extends JNIWrapper { public static native int registerInitializedCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -44,4 +45,7 @@ public static native int registerCurrentCallback( public static native void setCurrent(int index, int channel, double current); public static native void resetData(int index); + + /** Utility class. */ + private PowerDistributionDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/REVPHDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/REVPHDataJNI.java index 96b9701c272..9609f3dc22f 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/REVPHDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/REVPHDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for REV PH data. */ public class REVPHDataJNI extends JNIWrapper { public static native int registerInitializedCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -68,4 +69,7 @@ public static native void registerAllSolenoidCallbacks( int index, int channel, NotifyCallback callback, boolean initialNotify); public static native void resetData(int index); + + /** Utility class. */ + private REVPHDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/RelayDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/RelayDataJNI.java index 3b165e2754d..734371522b6 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/RelayDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/RelayDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for relay data. */ public class RelayDataJNI extends JNIWrapper { public static native int registerInitializedForwardCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -44,4 +45,7 @@ public static native int registerReverseCallback( public static native void setReverse(int index, boolean reverse); public static native void resetData(int index); + + /** Utility class. */ + private RelayDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java index f5362279163..83d599db2f1 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for roboRIO data. */ public class RoboRioDataJNI extends JNIWrapper { public static native int registerFPGAButtonCallback( NotifyCallback callback, boolean initialNotify); @@ -186,4 +187,7 @@ public static native int registerRadioLEDStateCallback( public static native void setRadioLEDState(int state); public static native void resetData(); + + /** Utility class. */ + private RoboRioDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/SPIAccelerometerDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/SPIAccelerometerDataJNI.java index 9ceaad8c403..b38fb553314 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/SPIAccelerometerDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/SPIAccelerometerDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for SPI accelerometer data. */ public class SPIAccelerometerDataJNI extends JNIWrapper { public static native int registerActiveCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -53,4 +54,7 @@ public static native int registerZCallback( public static native void setZ(int index, double z); public static native void resetData(int index); + + /** Utility class. */ + private SPIAccelerometerDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/SPIDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/SPIDataJNI.java index 11b777e2621..fa8df895df2 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/SPIDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/SPIDataJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for SPI data. */ public class SPIDataJNI extends JNIWrapper { public static native int registerInitializedCallback( int index, NotifyCallback callback, boolean initialNotify); @@ -30,4 +31,7 @@ public static native int registerReadAutoReceiveBufferCallback( public static native void cancelReadAutoReceiveBufferCallback(int index, int uid); public static native void resetData(int index); + + /** Utility class. */ + private SPIDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/SimDeviceDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/SimDeviceDataJNI.java index 092b955dc68..b4abb3306e8 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/SimDeviceDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/SimDeviceDataJNI.java @@ -7,6 +7,7 @@ import edu.wpi.first.hal.HALValue; import edu.wpi.first.hal.JNIWrapper; +/** JNI for sim device data. */ public class SimDeviceDataJNI extends JNIWrapper { public static native void setSimDeviceEnabled(String prefix, boolean enabled); @@ -106,4 +107,7 @@ public SimValueInfo( public static native double[] getSimValueEnumDoubleValues(int handle); public static native void resetSimDeviceData(); + + /** Utility class. */ + private SimDeviceDataJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/SimulatorJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/SimulatorJNI.java index 3dfeb068a98..298929c06f8 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/SimulatorJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/SimulatorJNI.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.JNIWrapper; +/** JNI for simulator. */ public class SimulatorJNI extends JNIWrapper { public static native void setRuntimeType(int type); @@ -28,4 +29,7 @@ public class SimulatorJNI extends JNIWrapper { public static native void stepTimingAsync(long delta); public static native void resetHandles(); + + /** Utility class. */ + private SimulatorJNI() {} } diff --git a/hal/src/main/java/edu/wpi/first/hal/util/UncleanStatusException.java b/hal/src/main/java/edu/wpi/first/hal/util/UncleanStatusException.java index 7be73b4f1bb..6c188d3752b 100644 --- a/hal/src/main/java/edu/wpi/first/hal/util/UncleanStatusException.java +++ b/hal/src/main/java/edu/wpi/first/hal/util/UncleanStatusException.java @@ -6,6 +6,7 @@ /** Exception for bad status codes from the chip object. */ public final class UncleanStatusException extends IllegalStateException { + /** The status code from the chip object. */ private final int m_statusCode; /** diff --git a/hal/src/main/native/athena/AddressableLED.cpp b/hal/src/main/native/athena/AddressableLED.cpp index 57f37cbf75e..05fe5def2b4 100644 --- a/hal/src/main/native/athena/AddressableLED.cpp +++ b/hal/src/main/native/athena/AddressableLED.cpp @@ -198,6 +198,10 @@ void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle, return; } + if (length == 0) { + return; + } + std::memcpy(led->ledBuffer, data, length * sizeof(HAL_AddressableLEDData)); asm("dmb"); diff --git a/hal/src/main/native/athena/DMA.cpp b/hal/src/main/native/athena/DMA.cpp index 23e92642484..851b09d935b 100644 --- a/hal/src/main/native/athena/DMA.cpp +++ b/hal/src/main/native/athena/DMA.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include "AnalogInternal.h" #include "ConstantsInternal.h" #include "DigitalInternal.h" @@ -676,7 +678,7 @@ void HAL_StartDMA(HAL_DMAHandle handle, int32_t queueDepth, int32_t* status) { SET_SIZE(Enable_DutyCycle_Low); SET_SIZE(Enable_DutyCycle_High); #undef SET_SIZE - dma->captureStore.captureSize = accum_size + 1; + dma->captureStore.captureSize = accum_size + 2; } uint32_t byteDepth = queueDepth * dma->captureStore.captureSize; @@ -734,12 +736,22 @@ enum HAL_DMAReadStatus HAL_ReadDMADirect(void* dmaPointer, static_cast(timeoutSeconds * 1000), &remainingBytes, status); + if ((remainingBytes % dma->captureStore.captureSize) != 0) { + fmt::print( + "Remaining bytes {} is not a multiple of capture size {}. This is " + "likely a " + "bug in WPILib. Please report this issue with a copy of your code.\n", + remainingBytes, dma->captureStore.captureSize); + } + *remainingOut = remainingBytes / dma->captureStore.captureSize; if (*status == 0) { - uint32_t lower_sample = + uint64_t upper_sample = dmaSample->readBuffer[dma->captureStore.captureSize - 1]; - dmaSample->timeStamp = HAL_ExpandFPGATime(lower_sample, status); + uint64_t lower_sample = + dmaSample->readBuffer[dma->captureStore.captureSize - 2]; + dmaSample->timeStamp = (upper_sample << 32) + lower_sample; if (*status != 0) { return HAL_DMA_ERROR; } diff --git a/hal/src/main/native/athena/rev/PHFrames.cpp b/hal/src/main/native/athena/rev/PHFrames.cpp index d098c004fc8..0d108c25e66 100644 --- a/hal/src/main/native/athena/rev/PHFrames.cpp +++ b/hal/src/main/native/athena/rev/PHFrames.cpp @@ -1,2071 +1,2071 @@ -/** - * The MIT License (MIT) - * - * Copyright (c) 2018-2019 Erik Moqvist - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * This file was generated by cantools version - */ - -#include - -#include "PHFrames.h" - -static inline uint8_t pack_left_shift_u8( - uint8_t value, - uint8_t shift, - uint8_t mask) -{ - return (uint8_t)((uint8_t)(value << shift) & mask); -} - -static inline uint8_t pack_left_shift_u16( - uint16_t value, - uint8_t shift, - uint8_t mask) -{ - return (uint8_t)((uint8_t)(value << shift) & mask); -} - -static inline uint8_t pack_left_shift_u32( - uint32_t value, - uint8_t shift, - uint8_t mask) -{ - return (uint8_t)((uint8_t)(value << shift) & mask); -} - -static inline uint8_t pack_right_shift_u16( - uint16_t value, - uint8_t shift, - uint8_t mask) -{ - return (uint8_t)((uint8_t)(value >> shift) & mask); -} - -static inline uint8_t pack_right_shift_u32( - uint32_t value, - uint8_t shift, - uint8_t mask) -{ - return (uint8_t)((uint8_t)(value >> shift) & mask); -} - -static inline uint16_t unpack_left_shift_u16( - uint8_t value, - uint8_t shift, - uint8_t mask) -{ - return (uint16_t)((uint16_t)(value & mask) << shift); -} - -static inline uint32_t unpack_left_shift_u32( - uint8_t value, - uint8_t shift, - uint8_t mask) -{ - return (uint32_t)((uint32_t)(value & mask) << shift); -} - -static inline uint8_t unpack_right_shift_u8( - uint8_t value, - uint8_t shift, - uint8_t mask) -{ - return (uint8_t)((uint8_t)(value & mask) >> shift); -} - -static inline uint16_t unpack_right_shift_u16( - uint8_t value, - uint8_t shift, - uint8_t mask) -{ - return (uint16_t)((uint16_t)(value & mask) >> shift); -} - -static inline uint32_t unpack_right_shift_u32( - uint8_t value, - uint8_t shift, - uint8_t mask) -{ - return (uint32_t)((uint32_t)(value & mask) >> shift); -} - -int PH_compressor_config_pack( - uint8_t *dst_p, - const struct PH_compressor_config_t *src_p, - size_t size) -{ - if (size < 5u) { - return (-EINVAL); - } - - memset(&dst_p[0], 0, 5); - - dst_p[0] |= pack_left_shift_u16(src_p->minimum_tank_pressure, 0u, 0xffu); - dst_p[1] |= pack_right_shift_u16(src_p->minimum_tank_pressure, 8u, 0xffu); - dst_p[2] |= pack_left_shift_u16(src_p->maximum_tank_pressure, 0u, 0xffu); - dst_p[3] |= pack_right_shift_u16(src_p->maximum_tank_pressure, 8u, 0xffu); - dst_p[4] |= pack_left_shift_u8(src_p->force_disable, 0u, 0x01u); - dst_p[4] |= pack_left_shift_u8(src_p->use_digital, 1u, 0x02u); - - return (5); -} - -int PH_compressor_config_unpack( - struct PH_compressor_config_t *dst_p, - const uint8_t *src_p, - size_t size) -{ - if (size < 5u) { - return (-EINVAL); - } - - dst_p->minimum_tank_pressure = unpack_right_shift_u16(src_p[0], 0u, 0xffu); - dst_p->minimum_tank_pressure |= unpack_left_shift_u16(src_p[1], 8u, 0xffu); - dst_p->maximum_tank_pressure = unpack_right_shift_u16(src_p[2], 0u, 0xffu); - dst_p->maximum_tank_pressure |= unpack_left_shift_u16(src_p[3], 8u, 0xffu); - dst_p->force_disable = unpack_right_shift_u8(src_p[4], 0u, 0x01u); - dst_p->use_digital = unpack_right_shift_u8(src_p[4], 1u, 0x02u); - - return (0); -} - -uint16_t PH_compressor_config_minimum_tank_pressure_encode(double value) -{ - return (uint16_t)(value / 0.001); -} - -double PH_compressor_config_minimum_tank_pressure_decode(uint16_t value) -{ - return ((double)value * 0.001); -} - -bool PH_compressor_config_minimum_tank_pressure_is_in_range(uint16_t value) -{ - return (value <= 5000u); -} - -uint16_t PH_compressor_config_maximum_tank_pressure_encode(double value) -{ - return (uint16_t)(value / 0.001); -} - -double PH_compressor_config_maximum_tank_pressure_decode(uint16_t value) -{ - return ((double)value * 0.001); -} - -bool PH_compressor_config_maximum_tank_pressure_is_in_range(uint16_t value) -{ - return (value <= 5000u); -} - -uint8_t PH_compressor_config_force_disable_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_compressor_config_force_disable_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_compressor_config_force_disable_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_compressor_config_use_digital_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_compressor_config_use_digital_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_compressor_config_use_digital_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -int PH_set_all_pack( - uint8_t *dst_p, - const struct PH_set_all_t *src_p, - size_t size) -{ - if (size < 4u) { - return (-EINVAL); - } - - memset(&dst_p[0], 0, 4); - - dst_p[0] |= pack_left_shift_u8(src_p->channel_0, 0u, 0x03u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_1, 2u, 0x0cu); - dst_p[0] |= pack_left_shift_u8(src_p->channel_2, 4u, 0x30u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_3, 6u, 0xc0u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_4, 0u, 0x03u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_5, 2u, 0x0cu); - dst_p[1] |= pack_left_shift_u8(src_p->channel_6, 4u, 0x30u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_7, 6u, 0xc0u); - dst_p[2] |= pack_left_shift_u8(src_p->channel_8, 0u, 0x03u); - dst_p[2] |= pack_left_shift_u8(src_p->channel_9, 2u, 0x0cu); - dst_p[2] |= pack_left_shift_u8(src_p->channel_10, 4u, 0x30u); - dst_p[2] |= pack_left_shift_u8(src_p->channel_11, 6u, 0xc0u); - dst_p[3] |= pack_left_shift_u8(src_p->channel_12, 0u, 0x03u); - dst_p[3] |= pack_left_shift_u8(src_p->channel_13, 2u, 0x0cu); - dst_p[3] |= pack_left_shift_u8(src_p->channel_14, 4u, 0x30u); - dst_p[3] |= pack_left_shift_u8(src_p->channel_15, 6u, 0xc0u); - - return (4); -} - -int PH_set_all_unpack( - struct PH_set_all_t *dst_p, - const uint8_t *src_p, - size_t size) -{ - if (size < 4u) { - return (-EINVAL); - } - - dst_p->channel_0 = unpack_right_shift_u8(src_p[0], 0u, 0x03u); - dst_p->channel_1 = unpack_right_shift_u8(src_p[0], 2u, 0x0cu); - dst_p->channel_2 = unpack_right_shift_u8(src_p[0], 4u, 0x30u); - dst_p->channel_3 = unpack_right_shift_u8(src_p[0], 6u, 0xc0u); - dst_p->channel_4 = unpack_right_shift_u8(src_p[1], 0u, 0x03u); - dst_p->channel_5 = unpack_right_shift_u8(src_p[1], 2u, 0x0cu); - dst_p->channel_6 = unpack_right_shift_u8(src_p[1], 4u, 0x30u); - dst_p->channel_7 = unpack_right_shift_u8(src_p[1], 6u, 0xc0u); - dst_p->channel_8 = unpack_right_shift_u8(src_p[2], 0u, 0x03u); - dst_p->channel_9 = unpack_right_shift_u8(src_p[2], 2u, 0x0cu); - dst_p->channel_10 = unpack_right_shift_u8(src_p[2], 4u, 0x30u); - dst_p->channel_11 = unpack_right_shift_u8(src_p[2], 6u, 0xc0u); - dst_p->channel_12 = unpack_right_shift_u8(src_p[3], 0u, 0x03u); - dst_p->channel_13 = unpack_right_shift_u8(src_p[3], 2u, 0x0cu); - dst_p->channel_14 = unpack_right_shift_u8(src_p[3], 4u, 0x30u); - dst_p->channel_15 = unpack_right_shift_u8(src_p[3], 6u, 0xc0u); - - return (0); -} - -uint8_t PH_set_all_channel_0_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_0_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_0_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_1_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_1_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_1_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_2_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_2_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_2_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_3_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_3_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_3_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_4_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_4_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_4_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_5_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_5_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_5_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_6_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_6_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_6_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_7_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_7_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_7_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_8_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_8_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_8_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_9_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_9_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_9_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_10_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_10_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_10_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_11_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_11_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_11_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_12_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_12_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_12_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_13_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_13_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_13_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_14_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_14_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_14_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -uint8_t PH_set_all_channel_15_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_set_all_channel_15_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_set_all_channel_15_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -int PH_pulse_once_pack( - uint8_t *dst_p, - const struct PH_pulse_once_t *src_p, - size_t size) -{ - if (size < 4u) { - return (-EINVAL); - } - - memset(&dst_p[0], 0, 4); - - dst_p[0] |= pack_left_shift_u8(src_p->channel_0, 0u, 0x01u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_1, 1u, 0x02u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_2, 2u, 0x04u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_3, 3u, 0x08u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_4, 4u, 0x10u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_5, 5u, 0x20u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_6, 6u, 0x40u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_7, 7u, 0x80u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_8, 0u, 0x01u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_9, 1u, 0x02u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_10, 2u, 0x04u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_11, 3u, 0x08u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_12, 4u, 0x10u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_13, 5u, 0x20u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_14, 6u, 0x40u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_15, 7u, 0x80u); - dst_p[2] |= pack_left_shift_u16(src_p->pulse_length_ms, 0u, 0xffu); - dst_p[3] |= pack_right_shift_u16(src_p->pulse_length_ms, 8u, 0xffu); - - return (4); -} - -int PH_pulse_once_unpack( - struct PH_pulse_once_t *dst_p, - const uint8_t *src_p, - size_t size) -{ - if (size < 4u) { - return (-EINVAL); - } - - dst_p->channel_0 = unpack_right_shift_u8(src_p[0], 0u, 0x01u); - dst_p->channel_1 = unpack_right_shift_u8(src_p[0], 1u, 0x02u); - dst_p->channel_2 = unpack_right_shift_u8(src_p[0], 2u, 0x04u); - dst_p->channel_3 = unpack_right_shift_u8(src_p[0], 3u, 0x08u); - dst_p->channel_4 = unpack_right_shift_u8(src_p[0], 4u, 0x10u); - dst_p->channel_5 = unpack_right_shift_u8(src_p[0], 5u, 0x20u); - dst_p->channel_6 = unpack_right_shift_u8(src_p[0], 6u, 0x40u); - dst_p->channel_7 = unpack_right_shift_u8(src_p[0], 7u, 0x80u); - dst_p->channel_8 = unpack_right_shift_u8(src_p[1], 0u, 0x01u); - dst_p->channel_9 = unpack_right_shift_u8(src_p[1], 1u, 0x02u); - dst_p->channel_10 = unpack_right_shift_u8(src_p[1], 2u, 0x04u); - dst_p->channel_11 = unpack_right_shift_u8(src_p[1], 3u, 0x08u); - dst_p->channel_12 = unpack_right_shift_u8(src_p[1], 4u, 0x10u); - dst_p->channel_13 = unpack_right_shift_u8(src_p[1], 5u, 0x20u); - dst_p->channel_14 = unpack_right_shift_u8(src_p[1], 6u, 0x40u); - dst_p->channel_15 = unpack_right_shift_u8(src_p[1], 7u, 0x80u); - dst_p->pulse_length_ms = unpack_right_shift_u16(src_p[2], 0u, 0xffu); - dst_p->pulse_length_ms |= unpack_left_shift_u16(src_p[3], 8u, 0xffu); - - return (0); -} - -uint8_t PH_pulse_once_channel_0_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_0_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_0_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_1_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_1_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_1_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_2_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_2_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_2_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_3_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_3_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_3_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_4_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_4_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_4_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_5_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_5_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_5_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_6_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_6_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_6_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_7_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_7_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_7_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_8_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_8_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_8_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_9_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_9_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_9_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_10_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_10_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_10_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_11_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_11_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_11_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_12_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_12_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_12_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_13_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_13_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_13_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_14_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_14_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_14_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_pulse_once_channel_15_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_pulse_once_channel_15_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_channel_15_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint16_t PH_pulse_once_pulse_length_ms_encode(double value) -{ - return (uint16_t)(value); -} - -double PH_pulse_once_pulse_length_ms_decode(uint16_t value) -{ - return ((double)value); -} - -bool PH_pulse_once_pulse_length_ms_is_in_range(uint16_t value) -{ - (void)value; - - return (true); -} - -int PH_status_0_pack( - uint8_t *dst_p, - const struct PH_status_0_t *src_p, - size_t size) -{ - if (size < 8u) { - return (-EINVAL); - } - - memset(&dst_p[0], 0, 8); - - dst_p[0] |= pack_left_shift_u8(src_p->channel_0, 0u, 0x01u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_1, 1u, 0x02u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_2, 2u, 0x04u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_3, 3u, 0x08u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_4, 4u, 0x10u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_5, 5u, 0x20u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_6, 6u, 0x40u); - dst_p[0] |= pack_left_shift_u8(src_p->channel_7, 7u, 0x80u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_8, 0u, 0x01u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_9, 1u, 0x02u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_10, 2u, 0x04u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_11, 3u, 0x08u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_12, 4u, 0x10u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_13, 5u, 0x20u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_14, 6u, 0x40u); - dst_p[1] |= pack_left_shift_u8(src_p->channel_15, 7u, 0x80u); - dst_p[2] |= pack_left_shift_u8(src_p->analog_0, 0u, 0xffu); - dst_p[3] |= pack_left_shift_u8(src_p->analog_1, 0u, 0xffu); - dst_p[4] |= pack_left_shift_u8(src_p->digital_sensor, 0u, 0x01u); - dst_p[4] |= pack_left_shift_u8(src_p->brownout_fault, 1u, 0x02u); - dst_p[4] |= pack_left_shift_u8(src_p->compressor_oc_fault, 2u, 0x04u); - dst_p[4] |= pack_left_shift_u8(src_p->compressor_open_fault, 3u, 0x08u); - dst_p[4] |= pack_left_shift_u8(src_p->solenoid_oc_fault, 4u, 0x10u); - dst_p[4] |= pack_left_shift_u8(src_p->can_warning_fault, 5u, 0x20u); - dst_p[4] |= pack_left_shift_u8(src_p->hardware_fault, 6u, 0x40u); - dst_p[5] |= pack_left_shift_u8(src_p->channel_0_fault, 0u, 0x01u); - dst_p[5] |= pack_left_shift_u8(src_p->channel_1_fault, 1u, 0x02u); - dst_p[5] |= pack_left_shift_u8(src_p->channel_2_fault, 2u, 0x04u); - dst_p[5] |= pack_left_shift_u8(src_p->channel_3_fault, 3u, 0x08u); - dst_p[5] |= pack_left_shift_u8(src_p->channel_4_fault, 4u, 0x10u); - dst_p[5] |= pack_left_shift_u8(src_p->channel_5_fault, 5u, 0x20u); - dst_p[5] |= pack_left_shift_u8(src_p->channel_6_fault, 6u, 0x40u); - dst_p[5] |= pack_left_shift_u8(src_p->channel_7_fault, 7u, 0x80u); - dst_p[6] |= pack_left_shift_u8(src_p->channel_8_fault, 0u, 0x01u); - dst_p[6] |= pack_left_shift_u8(src_p->channel_9_fault, 1u, 0x02u); - dst_p[6] |= pack_left_shift_u8(src_p->channel_10_fault, 2u, 0x04u); - dst_p[6] |= pack_left_shift_u8(src_p->channel_11_fault, 3u, 0x08u); - dst_p[6] |= pack_left_shift_u8(src_p->channel_12_fault, 4u, 0x10u); - dst_p[6] |= pack_left_shift_u8(src_p->channel_13_fault, 5u, 0x20u); - dst_p[6] |= pack_left_shift_u8(src_p->channel_14_fault, 6u, 0x40u); - dst_p[6] |= pack_left_shift_u8(src_p->channel_15_fault, 7u, 0x80u); - dst_p[7] |= pack_left_shift_u8(src_p->compressor_on, 0u, 0x01u); - dst_p[7] |= pack_left_shift_u8(src_p->system_enabled, 1u, 0x02u); - dst_p[7] |= pack_left_shift_u8(src_p->robo_rio_present, 2u, 0x04u); - dst_p[7] |= pack_left_shift_u8(src_p->compressor_config, 3u, 0x18u); - - return (8); -} - -int PH_status_0_unpack( - struct PH_status_0_t *dst_p, - const uint8_t *src_p, - size_t size) -{ - if (size < 8u) { - return (-EINVAL); - } - - dst_p->channel_0 = unpack_right_shift_u8(src_p[0], 0u, 0x01u); - dst_p->channel_1 = unpack_right_shift_u8(src_p[0], 1u, 0x02u); - dst_p->channel_2 = unpack_right_shift_u8(src_p[0], 2u, 0x04u); - dst_p->channel_3 = unpack_right_shift_u8(src_p[0], 3u, 0x08u); - dst_p->channel_4 = unpack_right_shift_u8(src_p[0], 4u, 0x10u); - dst_p->channel_5 = unpack_right_shift_u8(src_p[0], 5u, 0x20u); - dst_p->channel_6 = unpack_right_shift_u8(src_p[0], 6u, 0x40u); - dst_p->channel_7 = unpack_right_shift_u8(src_p[0], 7u, 0x80u); - dst_p->channel_8 = unpack_right_shift_u8(src_p[1], 0u, 0x01u); - dst_p->channel_9 = unpack_right_shift_u8(src_p[1], 1u, 0x02u); - dst_p->channel_10 = unpack_right_shift_u8(src_p[1], 2u, 0x04u); - dst_p->channel_11 = unpack_right_shift_u8(src_p[1], 3u, 0x08u); - dst_p->channel_12 = unpack_right_shift_u8(src_p[1], 4u, 0x10u); - dst_p->channel_13 = unpack_right_shift_u8(src_p[1], 5u, 0x20u); - dst_p->channel_14 = unpack_right_shift_u8(src_p[1], 6u, 0x40u); - dst_p->channel_15 = unpack_right_shift_u8(src_p[1], 7u, 0x80u); - dst_p->analog_0 = unpack_right_shift_u8(src_p[2], 0u, 0xffu); - dst_p->analog_1 = unpack_right_shift_u8(src_p[3], 0u, 0xffu); - dst_p->digital_sensor = unpack_right_shift_u8(src_p[4], 0u, 0x01u); - dst_p->brownout_fault = unpack_right_shift_u8(src_p[4], 1u, 0x02u); - dst_p->compressor_oc_fault = unpack_right_shift_u8(src_p[4], 2u, 0x04u); - dst_p->compressor_open_fault = unpack_right_shift_u8(src_p[4], 3u, 0x08u); - dst_p->solenoid_oc_fault = unpack_right_shift_u8(src_p[4], 4u, 0x10u); - dst_p->can_warning_fault = unpack_right_shift_u8(src_p[4], 5u, 0x20u); - dst_p->hardware_fault = unpack_right_shift_u8(src_p[4], 6u, 0x40u); - dst_p->channel_0_fault = unpack_right_shift_u8(src_p[5], 0u, 0x01u); - dst_p->channel_1_fault = unpack_right_shift_u8(src_p[5], 1u, 0x02u); - dst_p->channel_2_fault = unpack_right_shift_u8(src_p[5], 2u, 0x04u); - dst_p->channel_3_fault = unpack_right_shift_u8(src_p[5], 3u, 0x08u); - dst_p->channel_4_fault = unpack_right_shift_u8(src_p[5], 4u, 0x10u); - dst_p->channel_5_fault = unpack_right_shift_u8(src_p[5], 5u, 0x20u); - dst_p->channel_6_fault = unpack_right_shift_u8(src_p[5], 6u, 0x40u); - dst_p->channel_7_fault = unpack_right_shift_u8(src_p[5], 7u, 0x80u); - dst_p->channel_8_fault = unpack_right_shift_u8(src_p[6], 0u, 0x01u); - dst_p->channel_9_fault = unpack_right_shift_u8(src_p[6], 1u, 0x02u); - dst_p->channel_10_fault = unpack_right_shift_u8(src_p[6], 2u, 0x04u); - dst_p->channel_11_fault = unpack_right_shift_u8(src_p[6], 3u, 0x08u); - dst_p->channel_12_fault = unpack_right_shift_u8(src_p[6], 4u, 0x10u); - dst_p->channel_13_fault = unpack_right_shift_u8(src_p[6], 5u, 0x20u); - dst_p->channel_14_fault = unpack_right_shift_u8(src_p[6], 6u, 0x40u); - dst_p->channel_15_fault = unpack_right_shift_u8(src_p[6], 7u, 0x80u); - dst_p->compressor_on = unpack_right_shift_u8(src_p[7], 0u, 0x01u); - dst_p->system_enabled = unpack_right_shift_u8(src_p[7], 1u, 0x02u); - dst_p->robo_rio_present = unpack_right_shift_u8(src_p[7], 2u, 0x04u); - dst_p->compressor_config = unpack_right_shift_u8(src_p[7], 3u, 0x18u); - - return (0); -} - -uint8_t PH_status_0_channel_0_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_0_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_0_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_1_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_1_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_1_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_2_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_2_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_2_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_3_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_3_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_3_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_4_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_4_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_4_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_5_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_5_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_5_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_6_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_6_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_6_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_7_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_7_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_7_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_8_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_8_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_8_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_9_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_9_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_9_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_10_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_10_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_10_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_11_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_11_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_11_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_12_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_12_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_12_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_13_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_13_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_13_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_14_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_14_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_14_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_15_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_15_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_15_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_analog_0_encode(double value) -{ - return (uint8_t)(value / 0.01961); -} - -double PH_status_0_analog_0_decode(uint8_t value) -{ - return ((double)value * 0.01961); -} - -bool PH_status_0_analog_0_is_in_range(uint8_t value) -{ - (void)value; - - return (true); -} - -uint8_t PH_status_0_analog_1_encode(double value) -{ - return (uint8_t)(value / 0.01961); -} - -double PH_status_0_analog_1_decode(uint8_t value) -{ - return ((double)value * 0.01961); -} - -bool PH_status_0_analog_1_is_in_range(uint8_t value) -{ - (void)value; - - return (true); -} - -uint8_t PH_status_0_digital_sensor_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_digital_sensor_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_digital_sensor_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_brownout_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_brownout_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_brownout_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_compressor_oc_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_compressor_oc_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_compressor_oc_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_compressor_open_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_compressor_open_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_compressor_open_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_solenoid_oc_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_solenoid_oc_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_solenoid_oc_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_can_warning_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_can_warning_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_can_warning_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_hardware_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_hardware_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_hardware_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_0_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_0_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_0_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_1_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_1_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_1_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_2_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_2_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_2_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_3_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_3_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_3_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_4_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_4_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_4_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_5_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_5_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_5_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_6_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_6_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_6_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_7_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_7_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_7_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_8_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_8_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_8_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_9_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_9_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_9_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_10_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_10_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_10_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_11_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_11_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_11_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_12_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_12_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_12_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_13_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_13_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_13_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_14_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_14_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_14_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_channel_15_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_channel_15_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_channel_15_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_compressor_on_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_compressor_on_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_compressor_on_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_system_enabled_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_system_enabled_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_system_enabled_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_robo_rio_present_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_robo_rio_present_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_robo_rio_present_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_0_compressor_config_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_0_compressor_config_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_0_compressor_config_is_in_range(uint8_t value) -{ - return (value <= 3u); -} - -int PH_status_1_pack( - uint8_t *dst_p, - const struct PH_status_1_t *src_p, - size_t size) -{ - if (size < 8u) { - return (-EINVAL); - } - - memset(&dst_p[0], 0, 8); - - dst_p[0] |= pack_left_shift_u8(src_p->v_bus, 0u, 0xffu); - dst_p[1] |= pack_left_shift_u16(src_p->solenoid_voltage, 0u, 0xffu); - dst_p[2] |= pack_right_shift_u16(src_p->solenoid_voltage, 8u, 0x0fu); - dst_p[4] |= pack_left_shift_u8(src_p->compressor_current, 0u, 0xffu); - dst_p[5] |= pack_left_shift_u8(src_p->solenoid_current, 0u, 0xffu); - dst_p[6] |= pack_left_shift_u8(src_p->sticky_brownout_fault, 0u, 0x01u); - dst_p[6] |= pack_left_shift_u8(src_p->sticky_compressor_oc_fault, 1u, 0x02u); - dst_p[6] |= pack_left_shift_u8(src_p->sticky_compressor_open_fault, 2u, 0x04u); - dst_p[6] |= pack_left_shift_u8(src_p->sticky_solenoid_oc_fault, 3u, 0x08u); - dst_p[6] |= pack_left_shift_u8(src_p->sticky_can_warning_fault, 4u, 0x10u); - dst_p[6] |= pack_left_shift_u8(src_p->sticky_can_bus_off_fault, 5u, 0x20u); - dst_p[6] |= pack_left_shift_u8(src_p->sticky_hardware_fault, 6u, 0x40u); - dst_p[6] |= pack_left_shift_u8(src_p->sticky_firmware_fault, 7u, 0x80u); - dst_p[7] |= pack_left_shift_u8(src_p->sticky_has_reset_fault, 0u, 0x01u); - dst_p[7] |= pack_left_shift_u8(src_p->supply_voltage_5_v, 1u, 0xfeu); - - return (8); -} - -int PH_status_1_unpack( - struct PH_status_1_t *dst_p, - const uint8_t *src_p, - size_t size) -{ - if (size < 8u) { - return (-EINVAL); - } - - dst_p->v_bus = unpack_right_shift_u8(src_p[0], 0u, 0xffu); - dst_p->solenoid_voltage = unpack_right_shift_u16(src_p[1], 0u, 0xffu); - dst_p->solenoid_voltage |= unpack_left_shift_u16(src_p[2], 8u, 0x0fu); - dst_p->compressor_current = unpack_right_shift_u8(src_p[4], 0u, 0xffu); - dst_p->solenoid_current = unpack_right_shift_u8(src_p[5], 0u, 0xffu); - dst_p->sticky_brownout_fault = unpack_right_shift_u8(src_p[6], 0u, 0x01u); - dst_p->sticky_compressor_oc_fault = unpack_right_shift_u8(src_p[6], 1u, 0x02u); - dst_p->sticky_compressor_open_fault = unpack_right_shift_u8(src_p[6], 2u, 0x04u); - dst_p->sticky_solenoid_oc_fault = unpack_right_shift_u8(src_p[6], 3u, 0x08u); - dst_p->sticky_can_warning_fault = unpack_right_shift_u8(src_p[6], 4u, 0x10u); - dst_p->sticky_can_bus_off_fault = unpack_right_shift_u8(src_p[6], 5u, 0x20u); - dst_p->sticky_hardware_fault = unpack_right_shift_u8(src_p[6], 6u, 0x40u); - dst_p->sticky_firmware_fault = unpack_right_shift_u8(src_p[6], 7u, 0x80u); - dst_p->sticky_has_reset_fault = unpack_right_shift_u8(src_p[7], 0u, 0x01u); - dst_p->supply_voltage_5_v = unpack_right_shift_u8(src_p[7], 1u, 0xfeu); - - return (0); -} - -uint8_t PH_status_1_v_bus_encode(double value) -{ - return (uint8_t)((value - 4.0) / 0.0625); -} - -double PH_status_1_v_bus_decode(uint8_t value) -{ - return (((double)value * 0.0625) + 4.0); -} - -bool PH_status_1_v_bus_is_in_range(uint8_t value) -{ - return (value <= 192u); -} - -uint16_t PH_status_1_solenoid_voltage_encode(double value) -{ - return (uint16_t)(value / 0.0078125); -} - -double PH_status_1_solenoid_voltage_decode(uint16_t value) -{ - return ((double)value * 0.0078125); -} - -bool PH_status_1_solenoid_voltage_is_in_range(uint16_t value) -{ - return (value <= 4096u); -} - -uint8_t PH_status_1_compressor_current_encode(double value) -{ - return (uint8_t)(value / 0.125); -} - -double PH_status_1_compressor_current_decode(uint8_t value) -{ - return ((double)value * 0.125); -} - -bool PH_status_1_compressor_current_is_in_range(uint8_t value) -{ - (void)value; - - return (true); -} - -uint8_t PH_status_1_solenoid_current_encode(double value) -{ - return (uint8_t)(value / 0.125); -} - -double PH_status_1_solenoid_current_decode(uint8_t value) -{ - return ((double)value * 0.125); -} - -bool PH_status_1_solenoid_current_is_in_range(uint8_t value) -{ - (void)value; - - return (true); -} - -uint8_t PH_status_1_sticky_brownout_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_1_sticky_brownout_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_1_sticky_brownout_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_1_sticky_compressor_oc_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_1_sticky_compressor_oc_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_1_sticky_compressor_oc_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_1_sticky_compressor_open_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_1_sticky_compressor_open_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_1_sticky_compressor_open_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_1_sticky_solenoid_oc_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_1_sticky_solenoid_oc_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_1_sticky_solenoid_oc_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_1_sticky_can_warning_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_1_sticky_can_warning_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_1_sticky_can_warning_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_1_sticky_can_bus_off_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_1_sticky_can_bus_off_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_1_sticky_can_bus_off_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_1_sticky_hardware_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_1_sticky_hardware_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_1_sticky_hardware_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_1_sticky_firmware_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_1_sticky_firmware_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_1_sticky_firmware_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_1_sticky_has_reset_fault_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_status_1_sticky_has_reset_fault_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_status_1_sticky_has_reset_fault_is_in_range(uint8_t value) -{ - return (value <= 1u); -} - -uint8_t PH_status_1_supply_voltage_5_v_encode(double value) -{ - return (uint8_t)((value - 4.5) / 0.0078125); -} - -double PH_status_1_supply_voltage_5_v_decode(uint8_t value) -{ - return (((double)value * 0.0078125) + 4.5); -} - -bool PH_status_1_supply_voltage_5_v_is_in_range(uint8_t value) -{ - return (value <= 128u); -} - -int PH_clear_faults_pack( - uint8_t *dst_p, - const struct PH_clear_faults_t *src_p, - size_t size) -{ - (void)dst_p; - (void)src_p; - (void)size; - - return (0); -} - -int PH_clear_faults_unpack( - struct PH_clear_faults_t *dst_p, - const uint8_t *src_p, - size_t size) -{ - (void)dst_p; - (void)src_p; - (void)size; - - return (0); -} - -int PH_version_pack( - uint8_t *dst_p, - const struct PH_version_t *src_p, - size_t size) -{ - if (size < 8u) { - return (-EINVAL); - } - - memset(&dst_p[0], 0, 8); - - dst_p[0] |= pack_left_shift_u8(src_p->firmware_fix, 0u, 0xffu); - dst_p[1] |= pack_left_shift_u8(src_p->firmware_minor, 0u, 0xffu); - dst_p[2] |= pack_left_shift_u8(src_p->firmware_year, 0u, 0xffu); - dst_p[3] |= pack_left_shift_u8(src_p->hardware_minor, 0u, 0xffu); - dst_p[4] |= pack_left_shift_u8(src_p->hardware_major, 0u, 0xffu); - dst_p[5] |= pack_left_shift_u32(src_p->unique_id, 0u, 0xffu); - dst_p[6] |= pack_right_shift_u32(src_p->unique_id, 8u, 0xffu); - dst_p[7] |= pack_right_shift_u32(src_p->unique_id, 16u, 0xffu); - - return (8); -} - -int PH_version_unpack( - struct PH_version_t *dst_p, - const uint8_t *src_p, - size_t size) -{ - if (size < 8u) { - return (-EINVAL); - } - - dst_p->firmware_fix = unpack_right_shift_u8(src_p[0], 0u, 0xffu); - dst_p->firmware_minor = unpack_right_shift_u8(src_p[1], 0u, 0xffu); - dst_p->firmware_year = unpack_right_shift_u8(src_p[2], 0u, 0xffu); - dst_p->hardware_minor = unpack_right_shift_u8(src_p[3], 0u, 0xffu); - dst_p->hardware_major = unpack_right_shift_u8(src_p[4], 0u, 0xffu); - dst_p->unique_id = unpack_right_shift_u32(src_p[5], 0u, 0xffu); - dst_p->unique_id |= unpack_left_shift_u32(src_p[6], 8u, 0xffu); - dst_p->unique_id |= unpack_left_shift_u32(src_p[7], 16u, 0xffu); - - return (0); -} - -uint8_t PH_version_firmware_fix_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_version_firmware_fix_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_version_firmware_fix_is_in_range(uint8_t value) -{ - (void)value; - - return (true); -} - -uint8_t PH_version_firmware_minor_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_version_firmware_minor_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_version_firmware_minor_is_in_range(uint8_t value) -{ - (void)value; - - return (true); -} - -uint8_t PH_version_firmware_year_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_version_firmware_year_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_version_firmware_year_is_in_range(uint8_t value) -{ - (void)value; - - return (true); -} - -uint8_t PH_version_hardware_minor_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_version_hardware_minor_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_version_hardware_minor_is_in_range(uint8_t value) -{ - (void)value; - - return (true); -} - -uint8_t PH_version_hardware_major_encode(double value) -{ - return (uint8_t)(value); -} - -double PH_version_hardware_major_decode(uint8_t value) -{ - return ((double)value); -} - -bool PH_version_hardware_major_is_in_range(uint8_t value) -{ - (void)value; - - return (true); -} - -uint32_t PH_version_unique_id_encode(double value) -{ - return (uint32_t)(value); -} - -double PH_version_unique_id_decode(uint32_t value) -{ - return ((double)value); -} - -bool PH_version_unique_id_is_in_range(uint32_t value) -{ - return (value <= 16777215u); -} +/** + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Erik Moqvist + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * This file was generated by cantools version + */ + +#include + +#include "PHFrames.h" + +static inline uint8_t pack_left_shift_u8( + uint8_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint8_t)((uint8_t)(value << shift) & mask); +} + +static inline uint8_t pack_left_shift_u16( + uint16_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint8_t)((uint8_t)(value << shift) & mask); +} + +static inline uint8_t pack_left_shift_u32( + uint32_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint8_t)((uint8_t)(value << shift) & mask); +} + +static inline uint8_t pack_right_shift_u16( + uint16_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint8_t)((uint8_t)(value >> shift) & mask); +} + +static inline uint8_t pack_right_shift_u32( + uint32_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint8_t)((uint8_t)(value >> shift) & mask); +} + +static inline uint16_t unpack_left_shift_u16( + uint8_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint16_t)((uint16_t)(value & mask) << shift); +} + +static inline uint32_t unpack_left_shift_u32( + uint8_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint32_t)((uint32_t)(value & mask) << shift); +} + +static inline uint8_t unpack_right_shift_u8( + uint8_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint8_t)((uint8_t)(value & mask) >> shift); +} + +static inline uint16_t unpack_right_shift_u16( + uint8_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint16_t)((uint16_t)(value & mask) >> shift); +} + +static inline uint32_t unpack_right_shift_u32( + uint8_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint32_t)((uint32_t)(value & mask) >> shift); +} + +int PH_compressor_config_pack( + uint8_t *dst_p, + const struct PH_compressor_config_t *src_p, + size_t size) +{ + if (size < 5u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 5); + + dst_p[0] |= pack_left_shift_u16(src_p->minimum_tank_pressure, 0u, 0xffu); + dst_p[1] |= pack_right_shift_u16(src_p->minimum_tank_pressure, 8u, 0xffu); + dst_p[2] |= pack_left_shift_u16(src_p->maximum_tank_pressure, 0u, 0xffu); + dst_p[3] |= pack_right_shift_u16(src_p->maximum_tank_pressure, 8u, 0xffu); + dst_p[4] |= pack_left_shift_u8(src_p->force_disable, 0u, 0x01u); + dst_p[4] |= pack_left_shift_u8(src_p->use_digital, 1u, 0x02u); + + return (5); +} + +int PH_compressor_config_unpack( + struct PH_compressor_config_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 5u) { + return (-EINVAL); + } + + dst_p->minimum_tank_pressure = unpack_right_shift_u16(src_p[0], 0u, 0xffu); + dst_p->minimum_tank_pressure |= unpack_left_shift_u16(src_p[1], 8u, 0xffu); + dst_p->maximum_tank_pressure = unpack_right_shift_u16(src_p[2], 0u, 0xffu); + dst_p->maximum_tank_pressure |= unpack_left_shift_u16(src_p[3], 8u, 0xffu); + dst_p->force_disable = unpack_right_shift_u8(src_p[4], 0u, 0x01u); + dst_p->use_digital = unpack_right_shift_u8(src_p[4], 1u, 0x02u); + + return (0); +} + +uint16_t PH_compressor_config_minimum_tank_pressure_encode(double value) +{ + return (uint16_t)(value / 0.001); +} + +double PH_compressor_config_minimum_tank_pressure_decode(uint16_t value) +{ + return ((double)value * 0.001); +} + +bool PH_compressor_config_minimum_tank_pressure_is_in_range(uint16_t value) +{ + return (value <= 5000u); +} + +uint16_t PH_compressor_config_maximum_tank_pressure_encode(double value) +{ + return (uint16_t)(value / 0.001); +} + +double PH_compressor_config_maximum_tank_pressure_decode(uint16_t value) +{ + return ((double)value * 0.001); +} + +bool PH_compressor_config_maximum_tank_pressure_is_in_range(uint16_t value) +{ + return (value <= 5000u); +} + +uint8_t PH_compressor_config_force_disable_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_compressor_config_force_disable_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_compressor_config_force_disable_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_compressor_config_use_digital_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_compressor_config_use_digital_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_compressor_config_use_digital_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +int PH_set_all_pack( + uint8_t *dst_p, + const struct PH_set_all_t *src_p, + size_t size) +{ + if (size < 4u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 4); + + dst_p[0] |= pack_left_shift_u8(src_p->channel_0, 0u, 0x03u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_1, 2u, 0x0cu); + dst_p[0] |= pack_left_shift_u8(src_p->channel_2, 4u, 0x30u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_3, 6u, 0xc0u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_4, 0u, 0x03u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_5, 2u, 0x0cu); + dst_p[1] |= pack_left_shift_u8(src_p->channel_6, 4u, 0x30u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_7, 6u, 0xc0u); + dst_p[2] |= pack_left_shift_u8(src_p->channel_8, 0u, 0x03u); + dst_p[2] |= pack_left_shift_u8(src_p->channel_9, 2u, 0x0cu); + dst_p[2] |= pack_left_shift_u8(src_p->channel_10, 4u, 0x30u); + dst_p[2] |= pack_left_shift_u8(src_p->channel_11, 6u, 0xc0u); + dst_p[3] |= pack_left_shift_u8(src_p->channel_12, 0u, 0x03u); + dst_p[3] |= pack_left_shift_u8(src_p->channel_13, 2u, 0x0cu); + dst_p[3] |= pack_left_shift_u8(src_p->channel_14, 4u, 0x30u); + dst_p[3] |= pack_left_shift_u8(src_p->channel_15, 6u, 0xc0u); + + return (4); +} + +int PH_set_all_unpack( + struct PH_set_all_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 4u) { + return (-EINVAL); + } + + dst_p->channel_0 = unpack_right_shift_u8(src_p[0], 0u, 0x03u); + dst_p->channel_1 = unpack_right_shift_u8(src_p[0], 2u, 0x0cu); + dst_p->channel_2 = unpack_right_shift_u8(src_p[0], 4u, 0x30u); + dst_p->channel_3 = unpack_right_shift_u8(src_p[0], 6u, 0xc0u); + dst_p->channel_4 = unpack_right_shift_u8(src_p[1], 0u, 0x03u); + dst_p->channel_5 = unpack_right_shift_u8(src_p[1], 2u, 0x0cu); + dst_p->channel_6 = unpack_right_shift_u8(src_p[1], 4u, 0x30u); + dst_p->channel_7 = unpack_right_shift_u8(src_p[1], 6u, 0xc0u); + dst_p->channel_8 = unpack_right_shift_u8(src_p[2], 0u, 0x03u); + dst_p->channel_9 = unpack_right_shift_u8(src_p[2], 2u, 0x0cu); + dst_p->channel_10 = unpack_right_shift_u8(src_p[2], 4u, 0x30u); + dst_p->channel_11 = unpack_right_shift_u8(src_p[2], 6u, 0xc0u); + dst_p->channel_12 = unpack_right_shift_u8(src_p[3], 0u, 0x03u); + dst_p->channel_13 = unpack_right_shift_u8(src_p[3], 2u, 0x0cu); + dst_p->channel_14 = unpack_right_shift_u8(src_p[3], 4u, 0x30u); + dst_p->channel_15 = unpack_right_shift_u8(src_p[3], 6u, 0xc0u); + + return (0); +} + +uint8_t PH_set_all_channel_0_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_0_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_0_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_1_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_1_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_1_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_2_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_2_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_2_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_3_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_3_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_3_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_4_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_4_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_4_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_5_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_5_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_5_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_6_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_6_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_6_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_7_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_7_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_7_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_8_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_8_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_8_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_9_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_9_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_9_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_10_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_10_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_10_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_11_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_11_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_11_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_12_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_12_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_12_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_13_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_13_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_13_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_14_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_14_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_14_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t PH_set_all_channel_15_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_set_all_channel_15_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_set_all_channel_15_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +int PH_pulse_once_pack( + uint8_t *dst_p, + const struct PH_pulse_once_t *src_p, + size_t size) +{ + if (size < 4u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 4); + + dst_p[0] |= pack_left_shift_u8(src_p->channel_0, 0u, 0x01u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_1, 1u, 0x02u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_2, 2u, 0x04u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_3, 3u, 0x08u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_4, 4u, 0x10u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_5, 5u, 0x20u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_6, 6u, 0x40u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_7, 7u, 0x80u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_8, 0u, 0x01u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_9, 1u, 0x02u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_10, 2u, 0x04u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_11, 3u, 0x08u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_12, 4u, 0x10u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_13, 5u, 0x20u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_14, 6u, 0x40u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_15, 7u, 0x80u); + dst_p[2] |= pack_left_shift_u16(src_p->pulse_length_ms, 0u, 0xffu); + dst_p[3] |= pack_right_shift_u16(src_p->pulse_length_ms, 8u, 0xffu); + + return (4); +} + +int PH_pulse_once_unpack( + struct PH_pulse_once_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 4u) { + return (-EINVAL); + } + + dst_p->channel_0 = unpack_right_shift_u8(src_p[0], 0u, 0x01u); + dst_p->channel_1 = unpack_right_shift_u8(src_p[0], 1u, 0x02u); + dst_p->channel_2 = unpack_right_shift_u8(src_p[0], 2u, 0x04u); + dst_p->channel_3 = unpack_right_shift_u8(src_p[0], 3u, 0x08u); + dst_p->channel_4 = unpack_right_shift_u8(src_p[0], 4u, 0x10u); + dst_p->channel_5 = unpack_right_shift_u8(src_p[0], 5u, 0x20u); + dst_p->channel_6 = unpack_right_shift_u8(src_p[0], 6u, 0x40u); + dst_p->channel_7 = unpack_right_shift_u8(src_p[0], 7u, 0x80u); + dst_p->channel_8 = unpack_right_shift_u8(src_p[1], 0u, 0x01u); + dst_p->channel_9 = unpack_right_shift_u8(src_p[1], 1u, 0x02u); + dst_p->channel_10 = unpack_right_shift_u8(src_p[1], 2u, 0x04u); + dst_p->channel_11 = unpack_right_shift_u8(src_p[1], 3u, 0x08u); + dst_p->channel_12 = unpack_right_shift_u8(src_p[1], 4u, 0x10u); + dst_p->channel_13 = unpack_right_shift_u8(src_p[1], 5u, 0x20u); + dst_p->channel_14 = unpack_right_shift_u8(src_p[1], 6u, 0x40u); + dst_p->channel_15 = unpack_right_shift_u8(src_p[1], 7u, 0x80u); + dst_p->pulse_length_ms = unpack_right_shift_u16(src_p[2], 0u, 0xffu); + dst_p->pulse_length_ms |= unpack_left_shift_u16(src_p[3], 8u, 0xffu); + + return (0); +} + +uint8_t PH_pulse_once_channel_0_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_0_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_0_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_1_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_1_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_1_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_2_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_2_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_2_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_3_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_3_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_3_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_4_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_4_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_4_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_5_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_5_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_5_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_6_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_6_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_6_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_7_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_7_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_7_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_8_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_8_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_8_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_9_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_9_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_9_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_10_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_10_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_10_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_11_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_11_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_11_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_12_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_12_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_12_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_13_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_13_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_13_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_14_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_14_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_14_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_pulse_once_channel_15_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_pulse_once_channel_15_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_channel_15_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint16_t PH_pulse_once_pulse_length_ms_encode(double value) +{ + return (uint16_t)(value); +} + +double PH_pulse_once_pulse_length_ms_decode(uint16_t value) +{ + return ((double)value); +} + +bool PH_pulse_once_pulse_length_ms_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +int PH_status_0_pack( + uint8_t *dst_p, + const struct PH_status_0_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_left_shift_u8(src_p->channel_0, 0u, 0x01u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_1, 1u, 0x02u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_2, 2u, 0x04u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_3, 3u, 0x08u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_4, 4u, 0x10u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_5, 5u, 0x20u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_6, 6u, 0x40u); + dst_p[0] |= pack_left_shift_u8(src_p->channel_7, 7u, 0x80u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_8, 0u, 0x01u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_9, 1u, 0x02u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_10, 2u, 0x04u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_11, 3u, 0x08u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_12, 4u, 0x10u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_13, 5u, 0x20u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_14, 6u, 0x40u); + dst_p[1] |= pack_left_shift_u8(src_p->channel_15, 7u, 0x80u); + dst_p[2] |= pack_left_shift_u8(src_p->analog_0, 0u, 0xffu); + dst_p[3] |= pack_left_shift_u8(src_p->analog_1, 0u, 0xffu); + dst_p[4] |= pack_left_shift_u8(src_p->digital_sensor, 0u, 0x01u); + dst_p[4] |= pack_left_shift_u8(src_p->brownout_fault, 1u, 0x02u); + dst_p[4] |= pack_left_shift_u8(src_p->compressor_oc_fault, 2u, 0x04u); + dst_p[4] |= pack_left_shift_u8(src_p->compressor_open_fault, 3u, 0x08u); + dst_p[4] |= pack_left_shift_u8(src_p->solenoid_oc_fault, 4u, 0x10u); + dst_p[4] |= pack_left_shift_u8(src_p->can_warning_fault, 5u, 0x20u); + dst_p[4] |= pack_left_shift_u8(src_p->hardware_fault, 6u, 0x40u); + dst_p[5] |= pack_left_shift_u8(src_p->channel_0_fault, 0u, 0x01u); + dst_p[5] |= pack_left_shift_u8(src_p->channel_1_fault, 1u, 0x02u); + dst_p[5] |= pack_left_shift_u8(src_p->channel_2_fault, 2u, 0x04u); + dst_p[5] |= pack_left_shift_u8(src_p->channel_3_fault, 3u, 0x08u); + dst_p[5] |= pack_left_shift_u8(src_p->channel_4_fault, 4u, 0x10u); + dst_p[5] |= pack_left_shift_u8(src_p->channel_5_fault, 5u, 0x20u); + dst_p[5] |= pack_left_shift_u8(src_p->channel_6_fault, 6u, 0x40u); + dst_p[5] |= pack_left_shift_u8(src_p->channel_7_fault, 7u, 0x80u); + dst_p[6] |= pack_left_shift_u8(src_p->channel_8_fault, 0u, 0x01u); + dst_p[6] |= pack_left_shift_u8(src_p->channel_9_fault, 1u, 0x02u); + dst_p[6] |= pack_left_shift_u8(src_p->channel_10_fault, 2u, 0x04u); + dst_p[6] |= pack_left_shift_u8(src_p->channel_11_fault, 3u, 0x08u); + dst_p[6] |= pack_left_shift_u8(src_p->channel_12_fault, 4u, 0x10u); + dst_p[6] |= pack_left_shift_u8(src_p->channel_13_fault, 5u, 0x20u); + dst_p[6] |= pack_left_shift_u8(src_p->channel_14_fault, 6u, 0x40u); + dst_p[6] |= pack_left_shift_u8(src_p->channel_15_fault, 7u, 0x80u); + dst_p[7] |= pack_left_shift_u8(src_p->compressor_on, 0u, 0x01u); + dst_p[7] |= pack_left_shift_u8(src_p->system_enabled, 1u, 0x02u); + dst_p[7] |= pack_left_shift_u8(src_p->robo_rio_present, 2u, 0x04u); + dst_p[7] |= pack_left_shift_u8(src_p->compressor_config, 3u, 0x18u); + + return (8); +} + +int PH_status_0_unpack( + struct PH_status_0_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->channel_0 = unpack_right_shift_u8(src_p[0], 0u, 0x01u); + dst_p->channel_1 = unpack_right_shift_u8(src_p[0], 1u, 0x02u); + dst_p->channel_2 = unpack_right_shift_u8(src_p[0], 2u, 0x04u); + dst_p->channel_3 = unpack_right_shift_u8(src_p[0], 3u, 0x08u); + dst_p->channel_4 = unpack_right_shift_u8(src_p[0], 4u, 0x10u); + dst_p->channel_5 = unpack_right_shift_u8(src_p[0], 5u, 0x20u); + dst_p->channel_6 = unpack_right_shift_u8(src_p[0], 6u, 0x40u); + dst_p->channel_7 = unpack_right_shift_u8(src_p[0], 7u, 0x80u); + dst_p->channel_8 = unpack_right_shift_u8(src_p[1], 0u, 0x01u); + dst_p->channel_9 = unpack_right_shift_u8(src_p[1], 1u, 0x02u); + dst_p->channel_10 = unpack_right_shift_u8(src_p[1], 2u, 0x04u); + dst_p->channel_11 = unpack_right_shift_u8(src_p[1], 3u, 0x08u); + dst_p->channel_12 = unpack_right_shift_u8(src_p[1], 4u, 0x10u); + dst_p->channel_13 = unpack_right_shift_u8(src_p[1], 5u, 0x20u); + dst_p->channel_14 = unpack_right_shift_u8(src_p[1], 6u, 0x40u); + dst_p->channel_15 = unpack_right_shift_u8(src_p[1], 7u, 0x80u); + dst_p->analog_0 = unpack_right_shift_u8(src_p[2], 0u, 0xffu); + dst_p->analog_1 = unpack_right_shift_u8(src_p[3], 0u, 0xffu); + dst_p->digital_sensor = unpack_right_shift_u8(src_p[4], 0u, 0x01u); + dst_p->brownout_fault = unpack_right_shift_u8(src_p[4], 1u, 0x02u); + dst_p->compressor_oc_fault = unpack_right_shift_u8(src_p[4], 2u, 0x04u); + dst_p->compressor_open_fault = unpack_right_shift_u8(src_p[4], 3u, 0x08u); + dst_p->solenoid_oc_fault = unpack_right_shift_u8(src_p[4], 4u, 0x10u); + dst_p->can_warning_fault = unpack_right_shift_u8(src_p[4], 5u, 0x20u); + dst_p->hardware_fault = unpack_right_shift_u8(src_p[4], 6u, 0x40u); + dst_p->channel_0_fault = unpack_right_shift_u8(src_p[5], 0u, 0x01u); + dst_p->channel_1_fault = unpack_right_shift_u8(src_p[5], 1u, 0x02u); + dst_p->channel_2_fault = unpack_right_shift_u8(src_p[5], 2u, 0x04u); + dst_p->channel_3_fault = unpack_right_shift_u8(src_p[5], 3u, 0x08u); + dst_p->channel_4_fault = unpack_right_shift_u8(src_p[5], 4u, 0x10u); + dst_p->channel_5_fault = unpack_right_shift_u8(src_p[5], 5u, 0x20u); + dst_p->channel_6_fault = unpack_right_shift_u8(src_p[5], 6u, 0x40u); + dst_p->channel_7_fault = unpack_right_shift_u8(src_p[5], 7u, 0x80u); + dst_p->channel_8_fault = unpack_right_shift_u8(src_p[6], 0u, 0x01u); + dst_p->channel_9_fault = unpack_right_shift_u8(src_p[6], 1u, 0x02u); + dst_p->channel_10_fault = unpack_right_shift_u8(src_p[6], 2u, 0x04u); + dst_p->channel_11_fault = unpack_right_shift_u8(src_p[6], 3u, 0x08u); + dst_p->channel_12_fault = unpack_right_shift_u8(src_p[6], 4u, 0x10u); + dst_p->channel_13_fault = unpack_right_shift_u8(src_p[6], 5u, 0x20u); + dst_p->channel_14_fault = unpack_right_shift_u8(src_p[6], 6u, 0x40u); + dst_p->channel_15_fault = unpack_right_shift_u8(src_p[6], 7u, 0x80u); + dst_p->compressor_on = unpack_right_shift_u8(src_p[7], 0u, 0x01u); + dst_p->system_enabled = unpack_right_shift_u8(src_p[7], 1u, 0x02u); + dst_p->robo_rio_present = unpack_right_shift_u8(src_p[7], 2u, 0x04u); + dst_p->compressor_config = unpack_right_shift_u8(src_p[7], 3u, 0x18u); + + return (0); +} + +uint8_t PH_status_0_channel_0_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_0_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_0_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_1_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_1_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_1_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_2_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_2_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_2_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_3_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_3_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_3_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_4_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_4_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_4_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_5_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_5_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_5_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_6_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_6_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_6_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_7_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_7_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_7_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_8_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_8_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_8_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_9_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_9_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_9_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_10_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_10_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_10_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_11_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_11_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_11_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_12_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_12_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_12_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_13_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_13_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_13_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_14_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_14_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_14_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_15_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_15_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_15_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_analog_0_encode(double value) +{ + return (uint8_t)(value / 0.01961); +} + +double PH_status_0_analog_0_decode(uint8_t value) +{ + return ((double)value * 0.01961); +} + +bool PH_status_0_analog_0_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +uint8_t PH_status_0_analog_1_encode(double value) +{ + return (uint8_t)(value / 0.01961); +} + +double PH_status_0_analog_1_decode(uint8_t value) +{ + return ((double)value * 0.01961); +} + +bool PH_status_0_analog_1_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +uint8_t PH_status_0_digital_sensor_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_digital_sensor_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_digital_sensor_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_brownout_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_brownout_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_brownout_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_compressor_oc_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_compressor_oc_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_compressor_oc_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_compressor_open_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_compressor_open_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_compressor_open_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_solenoid_oc_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_solenoid_oc_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_solenoid_oc_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_can_warning_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_can_warning_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_can_warning_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_hardware_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_hardware_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_hardware_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_0_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_0_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_0_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_1_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_1_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_1_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_2_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_2_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_2_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_3_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_3_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_3_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_4_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_4_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_4_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_5_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_5_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_5_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_6_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_6_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_6_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_7_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_7_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_7_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_8_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_8_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_8_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_9_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_9_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_9_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_10_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_10_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_10_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_11_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_11_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_11_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_12_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_12_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_12_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_13_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_13_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_13_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_14_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_14_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_14_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_channel_15_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_channel_15_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_channel_15_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_compressor_on_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_compressor_on_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_compressor_on_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_system_enabled_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_system_enabled_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_system_enabled_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_robo_rio_present_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_robo_rio_present_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_robo_rio_present_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_0_compressor_config_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_0_compressor_config_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_0_compressor_config_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +int PH_status_1_pack( + uint8_t *dst_p, + const struct PH_status_1_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_left_shift_u8(src_p->v_bus, 0u, 0xffu); + dst_p[1] |= pack_left_shift_u16(src_p->solenoid_voltage, 0u, 0xffu); + dst_p[2] |= pack_right_shift_u16(src_p->solenoid_voltage, 8u, 0x0fu); + dst_p[4] |= pack_left_shift_u8(src_p->compressor_current, 0u, 0xffu); + dst_p[5] |= pack_left_shift_u8(src_p->solenoid_current, 0u, 0xffu); + dst_p[6] |= pack_left_shift_u8(src_p->sticky_brownout_fault, 0u, 0x01u); + dst_p[6] |= pack_left_shift_u8(src_p->sticky_compressor_oc_fault, 1u, 0x02u); + dst_p[6] |= pack_left_shift_u8(src_p->sticky_compressor_open_fault, 2u, 0x04u); + dst_p[6] |= pack_left_shift_u8(src_p->sticky_solenoid_oc_fault, 3u, 0x08u); + dst_p[6] |= pack_left_shift_u8(src_p->sticky_can_warning_fault, 4u, 0x10u); + dst_p[6] |= pack_left_shift_u8(src_p->sticky_can_bus_off_fault, 5u, 0x20u); + dst_p[6] |= pack_left_shift_u8(src_p->sticky_hardware_fault, 6u, 0x40u); + dst_p[6] |= pack_left_shift_u8(src_p->sticky_firmware_fault, 7u, 0x80u); + dst_p[7] |= pack_left_shift_u8(src_p->sticky_has_reset_fault, 0u, 0x01u); + dst_p[7] |= pack_left_shift_u8(src_p->supply_voltage_5_v, 1u, 0xfeu); + + return (8); +} + +int PH_status_1_unpack( + struct PH_status_1_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->v_bus = unpack_right_shift_u8(src_p[0], 0u, 0xffu); + dst_p->solenoid_voltage = unpack_right_shift_u16(src_p[1], 0u, 0xffu); + dst_p->solenoid_voltage |= unpack_left_shift_u16(src_p[2], 8u, 0x0fu); + dst_p->compressor_current = unpack_right_shift_u8(src_p[4], 0u, 0xffu); + dst_p->solenoid_current = unpack_right_shift_u8(src_p[5], 0u, 0xffu); + dst_p->sticky_brownout_fault = unpack_right_shift_u8(src_p[6], 0u, 0x01u); + dst_p->sticky_compressor_oc_fault = unpack_right_shift_u8(src_p[6], 1u, 0x02u); + dst_p->sticky_compressor_open_fault = unpack_right_shift_u8(src_p[6], 2u, 0x04u); + dst_p->sticky_solenoid_oc_fault = unpack_right_shift_u8(src_p[6], 3u, 0x08u); + dst_p->sticky_can_warning_fault = unpack_right_shift_u8(src_p[6], 4u, 0x10u); + dst_p->sticky_can_bus_off_fault = unpack_right_shift_u8(src_p[6], 5u, 0x20u); + dst_p->sticky_hardware_fault = unpack_right_shift_u8(src_p[6], 6u, 0x40u); + dst_p->sticky_firmware_fault = unpack_right_shift_u8(src_p[6], 7u, 0x80u); + dst_p->sticky_has_reset_fault = unpack_right_shift_u8(src_p[7], 0u, 0x01u); + dst_p->supply_voltage_5_v = unpack_right_shift_u8(src_p[7], 1u, 0xfeu); + + return (0); +} + +uint8_t PH_status_1_v_bus_encode(double value) +{ + return (uint8_t)((value - 4.0) / 0.0625); +} + +double PH_status_1_v_bus_decode(uint8_t value) +{ + return (((double)value * 0.0625) + 4.0); +} + +bool PH_status_1_v_bus_is_in_range(uint8_t value) +{ + return (value <= 192u); +} + +uint16_t PH_status_1_solenoid_voltage_encode(double value) +{ + return (uint16_t)(value / 0.0078125); +} + +double PH_status_1_solenoid_voltage_decode(uint16_t value) +{ + return ((double)value * 0.0078125); +} + +bool PH_status_1_solenoid_voltage_is_in_range(uint16_t value) +{ + return (value <= 4096u); +} + +uint8_t PH_status_1_compressor_current_encode(double value) +{ + return (uint8_t)(value / 0.125); +} + +double PH_status_1_compressor_current_decode(uint8_t value) +{ + return ((double)value * 0.125); +} + +bool PH_status_1_compressor_current_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +uint8_t PH_status_1_solenoid_current_encode(double value) +{ + return (uint8_t)(value / 0.125); +} + +double PH_status_1_solenoid_current_decode(uint8_t value) +{ + return ((double)value * 0.125); +} + +bool PH_status_1_solenoid_current_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +uint8_t PH_status_1_sticky_brownout_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_1_sticky_brownout_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_1_sticky_brownout_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_1_sticky_compressor_oc_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_1_sticky_compressor_oc_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_1_sticky_compressor_oc_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_1_sticky_compressor_open_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_1_sticky_compressor_open_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_1_sticky_compressor_open_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_1_sticky_solenoid_oc_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_1_sticky_solenoid_oc_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_1_sticky_solenoid_oc_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_1_sticky_can_warning_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_1_sticky_can_warning_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_1_sticky_can_warning_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_1_sticky_can_bus_off_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_1_sticky_can_bus_off_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_1_sticky_can_bus_off_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_1_sticky_hardware_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_1_sticky_hardware_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_1_sticky_hardware_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_1_sticky_firmware_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_1_sticky_firmware_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_1_sticky_firmware_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_1_sticky_has_reset_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_status_1_sticky_has_reset_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_status_1_sticky_has_reset_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t PH_status_1_supply_voltage_5_v_encode(double value) +{ + return (uint8_t)((value - 4.5) / 0.0078125); +} + +double PH_status_1_supply_voltage_5_v_decode(uint8_t value) +{ + return (((double)value * 0.0078125) + 4.5); +} + +bool PH_status_1_supply_voltage_5_v_is_in_range(uint8_t value) +{ + return (value <= 128u); +} + +int PH_clear_faults_pack( + uint8_t *dst_p, + const struct PH_clear_faults_t *src_p, + size_t size) +{ + (void)dst_p; + (void)src_p; + (void)size; + + return (0); +} + +int PH_clear_faults_unpack( + struct PH_clear_faults_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + (void)dst_p; + (void)src_p; + (void)size; + + return (0); +} + +int PH_version_pack( + uint8_t *dst_p, + const struct PH_version_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_left_shift_u8(src_p->firmware_fix, 0u, 0xffu); + dst_p[1] |= pack_left_shift_u8(src_p->firmware_minor, 0u, 0xffu); + dst_p[2] |= pack_left_shift_u8(src_p->firmware_year, 0u, 0xffu); + dst_p[3] |= pack_left_shift_u8(src_p->hardware_minor, 0u, 0xffu); + dst_p[4] |= pack_left_shift_u8(src_p->hardware_major, 0u, 0xffu); + dst_p[5] |= pack_left_shift_u32(src_p->unique_id, 0u, 0xffu); + dst_p[6] |= pack_right_shift_u32(src_p->unique_id, 8u, 0xffu); + dst_p[7] |= pack_right_shift_u32(src_p->unique_id, 16u, 0xffu); + + return (8); +} + +int PH_version_unpack( + struct PH_version_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->firmware_fix = unpack_right_shift_u8(src_p[0], 0u, 0xffu); + dst_p->firmware_minor = unpack_right_shift_u8(src_p[1], 0u, 0xffu); + dst_p->firmware_year = unpack_right_shift_u8(src_p[2], 0u, 0xffu); + dst_p->hardware_minor = unpack_right_shift_u8(src_p[3], 0u, 0xffu); + dst_p->hardware_major = unpack_right_shift_u8(src_p[4], 0u, 0xffu); + dst_p->unique_id = unpack_right_shift_u32(src_p[5], 0u, 0xffu); + dst_p->unique_id |= unpack_left_shift_u32(src_p[6], 8u, 0xffu); + dst_p->unique_id |= unpack_left_shift_u32(src_p[7], 16u, 0xffu); + + return (0); +} + +uint8_t PH_version_firmware_fix_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_version_firmware_fix_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_version_firmware_fix_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +uint8_t PH_version_firmware_minor_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_version_firmware_minor_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_version_firmware_minor_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +uint8_t PH_version_firmware_year_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_version_firmware_year_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_version_firmware_year_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +uint8_t PH_version_hardware_minor_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_version_hardware_minor_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_version_hardware_minor_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +uint8_t PH_version_hardware_major_encode(double value) +{ + return (uint8_t)(value); +} + +double PH_version_hardware_major_decode(uint8_t value) +{ + return ((double)value); +} + +bool PH_version_hardware_major_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +uint32_t PH_version_unique_id_encode(double value) +{ + return (uint32_t)(value); +} + +double PH_version_unique_id_decode(uint32_t value) +{ + return ((double)value); +} + +bool PH_version_unique_id_is_in_range(uint32_t value) +{ + return (value <= 16777215u); +} diff --git a/hal/src/main/native/athena/rev/PHFrames.h b/hal/src/main/native/athena/rev/PHFrames.h index a1849e1e6e4..cc9c23ccb3e 100644 --- a/hal/src/main/native/athena/rev/PHFrames.h +++ b/hal/src/main/native/athena/rev/PHFrames.h @@ -1,3816 +1,3816 @@ -/** - * The MIT License (MIT) - * - * Copyright (c) 2018-2019 Erik Moqvist - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * This file was generated by cantools version - */ - -#ifndef PH_H -#define PH_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -#ifndef EINVAL -# define EINVAL 22 -#endif - -/* Frame ids. */ -#define PH_COMPRESSOR_CONFIG_FRAME_ID (0x9050840u) -#define PH_SET_ALL_FRAME_ID (0x9050c00u) -#define PH_PULSE_ONCE_FRAME_ID (0x9050c40u) -#define PH_STATUS_0_FRAME_ID (0x9051800u) -#define PH_STATUS_1_FRAME_ID (0x9051840u) -#define PH_CLEAR_FAULTS_FRAME_ID (0x9051b80u) -#define PH_VERSION_FRAME_ID (0x9052600u) - -/* Frame lengths in bytes. */ -#define PH_COMPRESSOR_CONFIG_LENGTH (5u) -#define PH_SET_ALL_LENGTH (4u) -#define PH_PULSE_ONCE_LENGTH (4u) -#define PH_STATUS_0_LENGTH (8u) -#define PH_STATUS_1_LENGTH (8u) -#define PH_CLEAR_FAULTS_LENGTH (0u) -#define PH_VERSION_LENGTH (8u) - -/* Extended or standard frame types. */ -#define PH_COMPRESSOR_CONFIG_IS_EXTENDED (1) -#define PH_SET_ALL_IS_EXTENDED (1) -#define PH_PULSE_ONCE_IS_EXTENDED (1) -#define PH_STATUS_0_IS_EXTENDED (1) -#define PH_STATUS_1_IS_EXTENDED (1) -#define PH_CLEAR_FAULTS_IS_EXTENDED (1) -#define PH_VERSION_IS_EXTENDED (1) - -/* Frame cycle times in milliseconds. */ - - -/* Signal choices. */ - - -/** - * Signals in message Compressor_Config. - * - * Configures compressor to use digital/analog sensors - * - * All signal values are as on the CAN bus. - */ -struct PH_compressor_config_t { - /** - * Range: 0..5000 (0..5 V) - * Scale: 0.001 - * Offset: 0 - */ - uint16_t minimum_tank_pressure : 16; - - /** - * Range: 0..5000 (0..5 V) - * Scale: 0.001 - * Offset: 0 - */ - uint16_t maximum_tank_pressure : 16; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t force_disable : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t use_digital : 1; -}; - -/** - * Signals in message Set_All. - * - * Set state of all channels - * - * All signal values are as on the CAN bus. - */ -struct PH_set_all_t { - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_0 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_1 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_2 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_3 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_4 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_5 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_6 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_7 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_8 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_9 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_10 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_11 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_12 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_13 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_14 : 2; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_15 : 2; -}; - -/** - * Signals in message Pulse_Once. - * - * Pulse selected channels once - * - * All signal values are as on the CAN bus. - */ -struct PH_pulse_once_t { - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_0 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_1 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_2 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_3 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_4 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_5 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_6 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_7 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_8 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_9 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_10 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_11 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_12 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_13 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_14 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_15 : 1; - - /** - * Range: 0..65535 (0..65535 -) - * Scale: 1 - * Offset: 0 - */ - uint16_t pulse_length_ms : 16; -}; - -/** - * Signals in message Status_0. - * - * Periodic status frame 0 - * - * All signal values are as on the CAN bus. - */ -struct PH_status_0_t { - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_0 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_1 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_2 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_3 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_4 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_5 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_6 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_7 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_8 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_9 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_10 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_11 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_12 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_13 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_14 : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_15 : 1; - - /** - * Range: 0..255 (0..5.00055 V) - * Scale: 0.01961 - * Offset: 0 - */ - uint8_t analog_0 : 8; - - /** - * Range: 0..255 (0..5.00055 V) - * Scale: 0.01961 - * Offset: 0 - */ - uint8_t analog_1 : 8; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t digital_sensor : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t brownout_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t compressor_oc_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t compressor_open_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t solenoid_oc_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t can_warning_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t hardware_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_0_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_1_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_2_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_3_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_4_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_5_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_6_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_7_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_8_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_9_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_10_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_11_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_12_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_13_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_14_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t channel_15_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t compressor_on : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t system_enabled : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t robo_rio_present : 1; - - /** - * Range: 0..3 (0..3 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t compressor_config : 2; -}; - -/** - * Signals in message Status_1. - * - * Periodic status frame 1 - * - * All signal values are as on the CAN bus. - */ -struct PH_status_1_t { - /** - * Range: 0..192 (4..16 V) - * Scale: 0.0625 - * Offset: 4 - */ - uint8_t v_bus : 8; - - /** - * Range: 0..4096 (0..32 V) - * Scale: 0.0078125 - * Offset: 0 - */ - uint16_t solenoid_voltage : 12; - - /** - * Range: 0..256 (0..32 A) - * Scale: 0.125 - * Offset: 0 - */ - uint8_t compressor_current : 8; - - /** - * Range: 0..256 (0..32 A) - * Scale: 0.125 - * Offset: 0 - */ - uint8_t solenoid_current : 8; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t sticky_brownout_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t sticky_compressor_oc_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t sticky_compressor_open_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t sticky_solenoid_oc_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t sticky_can_warning_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t sticky_can_bus_off_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t sticky_hardware_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t sticky_firmware_fault : 1; - - /** - * Range: 0..1 (0..1 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t sticky_has_reset_fault : 1; - - /** - * Range: 0..128 (4.5..5.5 V) - * Scale: 0.0078125 - * Offset: 4.5 - */ - uint8_t supply_voltage_5_v : 7; -}; - -/** - * Signals in message Clear_Faults. - * - * Clear sticky faults on the device - * - * All signal values are as on the CAN bus. - */ -struct PH_clear_faults_t { - /** - * Dummy signal in empty message. - */ - uint8_t dummy; -}; - -/** - * Signals in message Version. - * - * Get the version of the PH - * - * All signal values are as on the CAN bus. - */ -struct PH_version_t { - /** - * Range: 0..255 (0..255 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t firmware_fix : 8; - - /** - * Range: 0..255 (0..255 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t firmware_minor : 8; - - /** - * Range: 0..255 (0..255 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t firmware_year : 8; - - /** - * Range: 0..255 (0..255 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t hardware_minor : 8; - - /** - * Range: 0..255 (0..255 -) - * Scale: 1 - * Offset: 0 - */ - uint8_t hardware_major : 8; - - /** - * Range: 0..16777215 (0..16777215 -) - * Scale: 1 - * Offset: 0 - */ - uint32_t unique_id : 24; -}; - -/** - * Pack message Compressor_Config. - * - * @param[out] dst_p Buffer to pack the message into. - * @param[in] src_p Data to pack. - * @param[in] size Size of dst_p. - * - * @return Size of packed data, or negative error code. - */ -int PH_compressor_config_pack( - uint8_t *dst_p, - const struct PH_compressor_config_t *src_p, - size_t size); - -/** - * Unpack message Compressor_Config. - * - * @param[out] dst_p Object to unpack the message into. - * @param[in] src_p Message to unpack. - * @param[in] size Size of src_p. - * - * @return zero(0) or negative error code. - */ -int PH_compressor_config_unpack( - struct PH_compressor_config_t *dst_p, - const uint8_t *src_p, - size_t size); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint16_t PH_compressor_config_minimum_tank_pressure_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_compressor_config_minimum_tank_pressure_decode(uint16_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_compressor_config_minimum_tank_pressure_is_in_range(uint16_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint16_t PH_compressor_config_maximum_tank_pressure_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_compressor_config_maximum_tank_pressure_decode(uint16_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_compressor_config_maximum_tank_pressure_is_in_range(uint16_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_compressor_config_force_disable_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_compressor_config_force_disable_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_compressor_config_force_disable_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_compressor_config_use_digital_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_compressor_config_use_digital_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_compressor_config_use_digital_is_in_range(uint8_t value); - -/** - * Pack message Set_All. - * - * @param[out] dst_p Buffer to pack the message into. - * @param[in] src_p Data to pack. - * @param[in] size Size of dst_p. - * - * @return Size of packed data, or negative error code. - */ -int PH_set_all_pack( - uint8_t *dst_p, - const struct PH_set_all_t *src_p, - size_t size); - -/** - * Unpack message Set_All. - * - * @param[out] dst_p Object to unpack the message into. - * @param[in] src_p Message to unpack. - * @param[in] size Size of src_p. - * - * @return zero(0) or negative error code. - */ -int PH_set_all_unpack( - struct PH_set_all_t *dst_p, - const uint8_t *src_p, - size_t size); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_0_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_0_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_0_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_1_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_1_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_1_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_2_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_2_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_2_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_3_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_3_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_3_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_4_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_4_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_4_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_5_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_5_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_5_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_6_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_6_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_6_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_7_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_7_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_7_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_8_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_8_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_8_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_9_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_9_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_9_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_10_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_10_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_10_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_11_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_11_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_11_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_12_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_12_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_12_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_13_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_13_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_13_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_14_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_14_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_14_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_set_all_channel_15_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_set_all_channel_15_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_set_all_channel_15_is_in_range(uint8_t value); - -/** - * Pack message Pulse_Once. - * - * @param[out] dst_p Buffer to pack the message into. - * @param[in] src_p Data to pack. - * @param[in] size Size of dst_p. - * - * @return Size of packed data, or negative error code. - */ -int PH_pulse_once_pack( - uint8_t *dst_p, - const struct PH_pulse_once_t *src_p, - size_t size); - -/** - * Unpack message Pulse_Once. - * - * @param[out] dst_p Object to unpack the message into. - * @param[in] src_p Message to unpack. - * @param[in] size Size of src_p. - * - * @return zero(0) or negative error code. - */ -int PH_pulse_once_unpack( - struct PH_pulse_once_t *dst_p, - const uint8_t *src_p, - size_t size); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_0_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_0_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_0_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_1_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_1_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_1_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_2_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_2_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_2_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_3_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_3_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_3_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_4_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_4_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_4_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_5_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_5_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_5_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_6_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_6_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_6_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_7_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_7_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_7_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_8_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_8_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_8_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_9_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_9_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_9_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_10_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_10_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_10_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_11_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_11_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_11_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_12_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_12_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_12_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_13_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_13_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_13_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_14_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_14_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_14_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_pulse_once_channel_15_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_channel_15_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_channel_15_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint16_t PH_pulse_once_pulse_length_ms_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_pulse_once_pulse_length_ms_decode(uint16_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_pulse_once_pulse_length_ms_is_in_range(uint16_t value); - -/** - * Pack message Status_0. - * - * @param[out] dst_p Buffer to pack the message into. - * @param[in] src_p Data to pack. - * @param[in] size Size of dst_p. - * - * @return Size of packed data, or negative error code. - */ -int PH_status_0_pack( - uint8_t *dst_p, - const struct PH_status_0_t *src_p, - size_t size); - -/** - * Unpack message Status_0. - * - * @param[out] dst_p Object to unpack the message into. - * @param[in] src_p Message to unpack. - * @param[in] size Size of src_p. - * - * @return zero(0) or negative error code. - */ -int PH_status_0_unpack( - struct PH_status_0_t *dst_p, - const uint8_t *src_p, - size_t size); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_0_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_0_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_0_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_1_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_1_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_1_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_2_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_2_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_2_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_3_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_3_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_3_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_4_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_4_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_4_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_5_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_5_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_5_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_6_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_6_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_6_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_7_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_7_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_7_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_8_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_8_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_8_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_9_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_9_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_9_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_10_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_10_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_10_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_11_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_11_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_11_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_12_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_12_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_12_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_13_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_13_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_13_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_14_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_14_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_14_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_15_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_15_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_15_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_analog_0_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_analog_0_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_analog_0_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_analog_1_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_analog_1_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_analog_1_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_digital_sensor_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_digital_sensor_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_digital_sensor_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_brownout_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_brownout_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_brownout_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_compressor_oc_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_compressor_oc_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_compressor_oc_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_compressor_open_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_compressor_open_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_compressor_open_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_solenoid_oc_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_solenoid_oc_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_solenoid_oc_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_can_warning_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_can_warning_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_can_warning_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_hardware_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_hardware_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_hardware_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_0_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_0_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_0_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_1_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_1_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_1_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_2_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_2_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_2_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_3_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_3_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_3_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_4_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_4_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_4_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_5_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_5_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_5_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_6_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_6_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_6_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_7_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_7_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_7_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_8_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_8_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_8_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_9_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_9_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_9_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_10_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_10_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_10_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_11_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_11_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_11_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_12_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_12_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_12_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_13_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_13_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_13_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_14_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_14_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_14_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_channel_15_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_channel_15_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_channel_15_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_compressor_on_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_compressor_on_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_compressor_on_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_system_enabled_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_system_enabled_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_system_enabled_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_robo_rio_present_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_robo_rio_present_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_robo_rio_present_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_0_compressor_config_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_0_compressor_config_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_0_compressor_config_is_in_range(uint8_t value); - -/** - * Pack message Status_1. - * - * @param[out] dst_p Buffer to pack the message into. - * @param[in] src_p Data to pack. - * @param[in] size Size of dst_p. - * - * @return Size of packed data, or negative error code. - */ -int PH_status_1_pack( - uint8_t *dst_p, - const struct PH_status_1_t *src_p, - size_t size); - -/** - * Unpack message Status_1. - * - * @param[out] dst_p Object to unpack the message into. - * @param[in] src_p Message to unpack. - * @param[in] size Size of src_p. - * - * @return zero(0) or negative error code. - */ -int PH_status_1_unpack( - struct PH_status_1_t *dst_p, - const uint8_t *src_p, - size_t size); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_1_v_bus_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_v_bus_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_v_bus_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint16_t PH_status_1_solenoid_voltage_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_solenoid_voltage_decode(uint16_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_solenoid_voltage_is_in_range(uint16_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_1_compressor_current_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_compressor_current_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_compressor_current_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_1_solenoid_current_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_solenoid_current_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_solenoid_current_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_1_sticky_brownout_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_sticky_brownout_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_sticky_brownout_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_1_sticky_compressor_oc_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_sticky_compressor_oc_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_sticky_compressor_oc_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_1_sticky_compressor_open_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_sticky_compressor_open_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_sticky_compressor_open_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_1_sticky_solenoid_oc_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_sticky_solenoid_oc_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_sticky_solenoid_oc_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_1_sticky_can_warning_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_sticky_can_warning_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_sticky_can_warning_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_1_sticky_can_bus_off_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_sticky_can_bus_off_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_sticky_can_bus_off_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_1_sticky_hardware_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_sticky_hardware_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_sticky_hardware_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_1_sticky_firmware_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_sticky_firmware_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_sticky_firmware_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_1_sticky_has_reset_fault_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_sticky_has_reset_fault_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_sticky_has_reset_fault_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_status_1_supply_voltage_5_v_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_status_1_supply_voltage_5_v_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_status_1_supply_voltage_5_v_is_in_range(uint8_t value); - -/** - * Pack message Clear_Faults. - * - * @param[out] dst_p Buffer to pack the message into. - * @param[in] src_p Data to pack. - * @param[in] size Size of dst_p. - * - * @return Size of packed data, or negative error code. - */ -int PH_clear_faults_pack( - uint8_t *dst_p, - const struct PH_clear_faults_t *src_p, - size_t size); - -/** - * Unpack message Clear_Faults. - * - * @param[out] dst_p Object to unpack the message into. - * @param[in] src_p Message to unpack. - * @param[in] size Size of src_p. - * - * @return zero(0) or negative error code. - */ -int PH_clear_faults_unpack( - struct PH_clear_faults_t *dst_p, - const uint8_t *src_p, - size_t size); - -/** - * Pack message Version. - * - * @param[out] dst_p Buffer to pack the message into. - * @param[in] src_p Data to pack. - * @param[in] size Size of dst_p. - * - * @return Size of packed data, or negative error code. - */ -int PH_version_pack( - uint8_t *dst_p, - const struct PH_version_t *src_p, - size_t size); - -/** - * Unpack message Version. - * - * @param[out] dst_p Object to unpack the message into. - * @param[in] src_p Message to unpack. - * @param[in] size Size of src_p. - * - * @return zero(0) or negative error code. - */ -int PH_version_unpack( - struct PH_version_t *dst_p, - const uint8_t *src_p, - size_t size); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_version_firmware_fix_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_version_firmware_fix_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_version_firmware_fix_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_version_firmware_minor_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_version_firmware_minor_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_version_firmware_minor_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_version_firmware_year_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_version_firmware_year_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_version_firmware_year_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_version_hardware_minor_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_version_hardware_minor_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_version_hardware_minor_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint8_t PH_version_hardware_major_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_version_hardware_major_decode(uint8_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_version_hardware_major_is_in_range(uint8_t value); - -/** - * Encode given signal by applying scaling and offset. - * - * @param[in] value Signal to encode. - * - * @return Encoded signal. - */ -uint32_t PH_version_unique_id_encode(double value); - -/** - * Decode given signal by applying scaling and offset. - * - * @param[in] value Signal to decode. - * - * @return Decoded signal. - */ -double PH_version_unique_id_decode(uint32_t value); - -/** - * Check that given signal is in allowed range. - * - * @param[in] value Signal to check. - * - * @return true if in range, false otherwise. - */ -bool PH_version_unique_id_is_in_range(uint32_t value); - - -#ifdef __cplusplus -} -#endif - -#endif +/** + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Erik Moqvist + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * This file was generated by cantools version + */ + +#ifndef PH_H +#define PH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#ifndef EINVAL +# define EINVAL 22 +#endif + +/* Frame ids. */ +#define PH_COMPRESSOR_CONFIG_FRAME_ID (0x9050840u) +#define PH_SET_ALL_FRAME_ID (0x9050c00u) +#define PH_PULSE_ONCE_FRAME_ID (0x9050c40u) +#define PH_STATUS_0_FRAME_ID (0x9051800u) +#define PH_STATUS_1_FRAME_ID (0x9051840u) +#define PH_CLEAR_FAULTS_FRAME_ID (0x9051b80u) +#define PH_VERSION_FRAME_ID (0x9052600u) + +/* Frame lengths in bytes. */ +#define PH_COMPRESSOR_CONFIG_LENGTH (5u) +#define PH_SET_ALL_LENGTH (4u) +#define PH_PULSE_ONCE_LENGTH (4u) +#define PH_STATUS_0_LENGTH (8u) +#define PH_STATUS_1_LENGTH (8u) +#define PH_CLEAR_FAULTS_LENGTH (0u) +#define PH_VERSION_LENGTH (8u) + +/* Extended or standard frame types. */ +#define PH_COMPRESSOR_CONFIG_IS_EXTENDED (1) +#define PH_SET_ALL_IS_EXTENDED (1) +#define PH_PULSE_ONCE_IS_EXTENDED (1) +#define PH_STATUS_0_IS_EXTENDED (1) +#define PH_STATUS_1_IS_EXTENDED (1) +#define PH_CLEAR_FAULTS_IS_EXTENDED (1) +#define PH_VERSION_IS_EXTENDED (1) + +/* Frame cycle times in milliseconds. */ + + +/* Signal choices. */ + + +/** + * Signals in message Compressor_Config. + * + * Configures compressor to use digital/analog sensors + * + * All signal values are as on the CAN bus. + */ +struct PH_compressor_config_t { + /** + * Range: 0..5000 (0..5 V) + * Scale: 0.001 + * Offset: 0 + */ + uint16_t minimum_tank_pressure : 16; + + /** + * Range: 0..5000 (0..5 V) + * Scale: 0.001 + * Offset: 0 + */ + uint16_t maximum_tank_pressure : 16; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t force_disable : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t use_digital : 1; +}; + +/** + * Signals in message Set_All. + * + * Set state of all channels + * + * All signal values are as on the CAN bus. + */ +struct PH_set_all_t { + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_0 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_1 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_2 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_3 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_4 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_5 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_6 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_7 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_8 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_9 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_10 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_11 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_12 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_13 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_14 : 2; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_15 : 2; +}; + +/** + * Signals in message Pulse_Once. + * + * Pulse selected channels once + * + * All signal values are as on the CAN bus. + */ +struct PH_pulse_once_t { + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_0 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_1 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_2 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_3 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_4 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_5 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_6 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_7 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_8 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_9 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_10 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_11 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_12 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_13 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_14 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_15 : 1; + + /** + * Range: 0..65535 (0..65535 -) + * Scale: 1 + * Offset: 0 + */ + uint16_t pulse_length_ms : 16; +}; + +/** + * Signals in message Status_0. + * + * Periodic status frame 0 + * + * All signal values are as on the CAN bus. + */ +struct PH_status_0_t { + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_0 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_1 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_2 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_3 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_4 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_5 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_6 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_7 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_8 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_9 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_10 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_11 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_12 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_13 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_14 : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_15 : 1; + + /** + * Range: 0..255 (0..5.00055 V) + * Scale: 0.01961 + * Offset: 0 + */ + uint8_t analog_0 : 8; + + /** + * Range: 0..255 (0..5.00055 V) + * Scale: 0.01961 + * Offset: 0 + */ + uint8_t analog_1 : 8; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t digital_sensor : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t brownout_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t compressor_oc_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t compressor_open_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t solenoid_oc_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t can_warning_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t hardware_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_0_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_1_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_2_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_3_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_4_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_5_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_6_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_7_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_8_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_9_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_10_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_11_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_12_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_13_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_14_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t channel_15_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t compressor_on : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t system_enabled : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t robo_rio_present : 1; + + /** + * Range: 0..3 (0..3 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t compressor_config : 2; +}; + +/** + * Signals in message Status_1. + * + * Periodic status frame 1 + * + * All signal values are as on the CAN bus. + */ +struct PH_status_1_t { + /** + * Range: 0..192 (4..16 V) + * Scale: 0.0625 + * Offset: 4 + */ + uint8_t v_bus : 8; + + /** + * Range: 0..4096 (0..32 V) + * Scale: 0.0078125 + * Offset: 0 + */ + uint16_t solenoid_voltage : 12; + + /** + * Range: 0..256 (0..32 A) + * Scale: 0.125 + * Offset: 0 + */ + uint8_t compressor_current : 8; + + /** + * Range: 0..256 (0..32 A) + * Scale: 0.125 + * Offset: 0 + */ + uint8_t solenoid_current : 8; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t sticky_brownout_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t sticky_compressor_oc_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t sticky_compressor_open_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t sticky_solenoid_oc_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t sticky_can_warning_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t sticky_can_bus_off_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t sticky_hardware_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t sticky_firmware_fault : 1; + + /** + * Range: 0..1 (0..1 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t sticky_has_reset_fault : 1; + + /** + * Range: 0..128 (4.5..5.5 V) + * Scale: 0.0078125 + * Offset: 4.5 + */ + uint8_t supply_voltage_5_v : 7; +}; + +/** + * Signals in message Clear_Faults. + * + * Clear sticky faults on the device + * + * All signal values are as on the CAN bus. + */ +struct PH_clear_faults_t { + /** + * Dummy signal in empty message. + */ + uint8_t dummy; +}; + +/** + * Signals in message Version. + * + * Get the version of the PH + * + * All signal values are as on the CAN bus. + */ +struct PH_version_t { + /** + * Range: 0..255 (0..255 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t firmware_fix : 8; + + /** + * Range: 0..255 (0..255 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t firmware_minor : 8; + + /** + * Range: 0..255 (0..255 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t firmware_year : 8; + + /** + * Range: 0..255 (0..255 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t hardware_minor : 8; + + /** + * Range: 0..255 (0..255 -) + * Scale: 1 + * Offset: 0 + */ + uint8_t hardware_major : 8; + + /** + * Range: 0..16777215 (0..16777215 -) + * Scale: 1 + * Offset: 0 + */ + uint32_t unique_id : 24; +}; + +/** + * Pack message Compressor_Config. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int PH_compressor_config_pack( + uint8_t *dst_p, + const struct PH_compressor_config_t *src_p, + size_t size); + +/** + * Unpack message Compressor_Config. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int PH_compressor_config_unpack( + struct PH_compressor_config_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t PH_compressor_config_minimum_tank_pressure_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_compressor_config_minimum_tank_pressure_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_compressor_config_minimum_tank_pressure_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t PH_compressor_config_maximum_tank_pressure_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_compressor_config_maximum_tank_pressure_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_compressor_config_maximum_tank_pressure_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_compressor_config_force_disable_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_compressor_config_force_disable_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_compressor_config_force_disable_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_compressor_config_use_digital_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_compressor_config_use_digital_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_compressor_config_use_digital_is_in_range(uint8_t value); + +/** + * Pack message Set_All. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int PH_set_all_pack( + uint8_t *dst_p, + const struct PH_set_all_t *src_p, + size_t size); + +/** + * Unpack message Set_All. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int PH_set_all_unpack( + struct PH_set_all_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_0_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_0_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_0_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_1_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_1_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_1_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_2_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_2_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_2_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_3_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_3_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_3_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_4_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_4_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_4_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_5_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_5_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_5_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_6_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_6_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_6_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_7_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_7_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_7_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_8_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_8_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_8_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_9_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_9_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_9_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_10_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_10_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_10_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_11_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_11_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_11_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_12_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_12_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_12_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_13_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_13_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_13_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_14_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_14_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_14_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_set_all_channel_15_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_set_all_channel_15_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_set_all_channel_15_is_in_range(uint8_t value); + +/** + * Pack message Pulse_Once. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int PH_pulse_once_pack( + uint8_t *dst_p, + const struct PH_pulse_once_t *src_p, + size_t size); + +/** + * Unpack message Pulse_Once. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int PH_pulse_once_unpack( + struct PH_pulse_once_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_0_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_0_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_0_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_1_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_1_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_1_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_2_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_2_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_2_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_3_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_3_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_3_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_4_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_4_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_4_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_5_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_5_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_5_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_6_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_6_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_6_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_7_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_7_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_7_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_8_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_8_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_8_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_9_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_9_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_9_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_10_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_10_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_10_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_11_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_11_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_11_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_12_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_12_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_12_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_13_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_13_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_13_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_14_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_14_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_14_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_pulse_once_channel_15_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_channel_15_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_channel_15_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t PH_pulse_once_pulse_length_ms_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_pulse_once_pulse_length_ms_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_pulse_once_pulse_length_ms_is_in_range(uint16_t value); + +/** + * Pack message Status_0. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int PH_status_0_pack( + uint8_t *dst_p, + const struct PH_status_0_t *src_p, + size_t size); + +/** + * Unpack message Status_0. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int PH_status_0_unpack( + struct PH_status_0_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_0_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_0_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_0_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_1_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_1_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_1_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_2_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_2_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_2_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_3_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_3_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_3_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_4_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_4_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_4_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_5_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_5_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_5_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_6_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_6_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_6_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_7_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_7_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_7_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_8_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_8_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_8_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_9_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_9_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_9_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_10_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_10_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_10_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_11_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_11_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_11_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_12_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_12_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_12_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_13_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_13_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_13_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_14_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_14_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_14_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_15_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_15_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_15_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_analog_0_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_analog_0_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_analog_0_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_analog_1_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_analog_1_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_analog_1_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_digital_sensor_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_digital_sensor_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_digital_sensor_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_brownout_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_brownout_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_brownout_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_compressor_oc_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_compressor_oc_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_compressor_oc_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_compressor_open_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_compressor_open_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_compressor_open_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_solenoid_oc_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_solenoid_oc_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_solenoid_oc_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_can_warning_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_can_warning_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_can_warning_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_hardware_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_hardware_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_hardware_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_0_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_0_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_0_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_1_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_1_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_1_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_2_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_2_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_2_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_3_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_3_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_3_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_4_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_4_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_4_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_5_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_5_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_5_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_6_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_6_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_6_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_7_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_7_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_7_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_8_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_8_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_8_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_9_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_9_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_9_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_10_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_10_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_10_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_11_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_11_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_11_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_12_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_12_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_12_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_13_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_13_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_13_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_14_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_14_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_14_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_channel_15_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_channel_15_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_channel_15_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_compressor_on_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_compressor_on_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_compressor_on_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_system_enabled_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_system_enabled_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_system_enabled_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_robo_rio_present_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_robo_rio_present_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_robo_rio_present_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_0_compressor_config_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_0_compressor_config_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_0_compressor_config_is_in_range(uint8_t value); + +/** + * Pack message Status_1. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int PH_status_1_pack( + uint8_t *dst_p, + const struct PH_status_1_t *src_p, + size_t size); + +/** + * Unpack message Status_1. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int PH_status_1_unpack( + struct PH_status_1_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_1_v_bus_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_v_bus_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_v_bus_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t PH_status_1_solenoid_voltage_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_solenoid_voltage_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_solenoid_voltage_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_1_compressor_current_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_compressor_current_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_compressor_current_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_1_solenoid_current_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_solenoid_current_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_solenoid_current_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_1_sticky_brownout_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_sticky_brownout_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_sticky_brownout_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_1_sticky_compressor_oc_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_sticky_compressor_oc_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_sticky_compressor_oc_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_1_sticky_compressor_open_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_sticky_compressor_open_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_sticky_compressor_open_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_1_sticky_solenoid_oc_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_sticky_solenoid_oc_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_sticky_solenoid_oc_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_1_sticky_can_warning_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_sticky_can_warning_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_sticky_can_warning_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_1_sticky_can_bus_off_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_sticky_can_bus_off_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_sticky_can_bus_off_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_1_sticky_hardware_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_sticky_hardware_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_sticky_hardware_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_1_sticky_firmware_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_sticky_firmware_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_sticky_firmware_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_1_sticky_has_reset_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_sticky_has_reset_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_sticky_has_reset_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_status_1_supply_voltage_5_v_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_status_1_supply_voltage_5_v_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_status_1_supply_voltage_5_v_is_in_range(uint8_t value); + +/** + * Pack message Clear_Faults. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int PH_clear_faults_pack( + uint8_t *dst_p, + const struct PH_clear_faults_t *src_p, + size_t size); + +/** + * Unpack message Clear_Faults. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int PH_clear_faults_unpack( + struct PH_clear_faults_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Pack message Version. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int PH_version_pack( + uint8_t *dst_p, + const struct PH_version_t *src_p, + size_t size); + +/** + * Unpack message Version. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int PH_version_unpack( + struct PH_version_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_version_firmware_fix_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_version_firmware_fix_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_version_firmware_fix_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_version_firmware_minor_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_version_firmware_minor_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_version_firmware_minor_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_version_firmware_year_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_version_firmware_year_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_version_firmware_year_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_version_hardware_minor_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_version_hardware_minor_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_version_hardware_minor_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t PH_version_hardware_major_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_version_hardware_major_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_version_hardware_major_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint32_t PH_version_unique_id_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double PH_version_unique_id_decode(uint32_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool PH_version_unique_id_is_in_range(uint32_t value); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hal/src/main/native/include/hal/CAN.h b/hal/src/main/native/include/hal/CAN.h index 77a6239d6e3..6d53cfaa942 100644 --- a/hal/src/main/native/include/hal/CAN.h +++ b/hal/src/main/native/include/hal/CAN.h @@ -14,11 +14,24 @@ // These are copies of defines located in CANSessionMux.h prepended with HAL_ +/** + * Flag for sending a CAN message once. + */ #define HAL_CAN_SEND_PERIOD_NO_REPEAT 0 + +/** + * Flag for stopping periodic CAN message sends. + */ #define HAL_CAN_SEND_PERIOD_STOP_REPEATING -1 -/* Flags in the upper bits of the messageID */ +/** + * Mask for "is frame remote" in message ID. + */ #define HAL_CAN_IS_FRAME_REMOTE 0x80000000 + +/** + * Mask for "is frame 11 bits" in message ID. + */ #define HAL_CAN_IS_FRAME_11BIT 0x40000000 #define HAL_ERR_CANSessionMux_InvalidBuffer -44086 diff --git a/hal/src/main/native/include/hal/CANAPITypes.h b/hal/src/main/native/include/hal/CANAPITypes.h index e4a44f4ca72..247732c8958 100644 --- a/hal/src/main/native/include/hal/CANAPITypes.h +++ b/hal/src/main/native/include/hal/CANAPITypes.h @@ -20,18 +20,31 @@ * Teams should use HAL_CAN_Dev_kMiscellaneous */ HAL_ENUM(HAL_CANDeviceType) { + /// Broadcast. HAL_CAN_Dev_kBroadcast = 0, + /// Robot controller. HAL_CAN_Dev_kRobotController = 1, + /// Motor controller. HAL_CAN_Dev_kMotorController = 2, + /// Relay controller. HAL_CAN_Dev_kRelayController = 3, + /// Gyro sensor. HAL_CAN_Dev_kGyroSensor = 4, + /// Accelerometer. HAL_CAN_Dev_kAccelerometer = 5, + /// Ultrasonic sensor. HAL_CAN_Dev_kUltrasonicSensor = 6, + /// Gear tooth sensor. HAL_CAN_Dev_kGearToothSensor = 7, + /// Power distribution. HAL_CAN_Dev_kPowerDistribution = 8, + /// Pneumatics. HAL_CAN_Dev_kPneumatics = 9, + /// Miscellaneous. HAL_CAN_Dev_kMiscellaneous = 10, + /// IO breakout. HAL_CAN_Dev_kIOBreakout = 11, + /// Firmware update. HAL_CAN_Dev_kFirmwareUpdate = 31 }; @@ -41,22 +54,39 @@ HAL_ENUM(HAL_CANDeviceType) { * Teams should use HAL_CAN_Man_kTeamUse. */ HAL_ENUM(HAL_CANManufacturer) { + /// Broadcast. HAL_CAN_Man_kBroadcast = 0, + /// National Instruments. HAL_CAN_Man_kNI = 1, + /// Luminary Micro. HAL_CAN_Man_kLM = 2, + /// DEKA Research and Development Corp. HAL_CAN_Man_kDEKA = 3, + /// Cross the Road Electronics. HAL_CAN_Man_kCTRE = 4, + /// REV robotics. HAL_CAN_Man_kREV = 5, + /// Grapple. HAL_CAN_Man_kGrapple = 6, + /// MindSensors. HAL_CAN_Man_kMS = 7, + /// Team use. HAL_CAN_Man_kTeamUse = 8, + /// Kauai Labs. HAL_CAN_Man_kKauaiLabs = 9, + /// Copperforge. HAL_CAN_Man_kCopperforge = 10, + /// Playing With Fusion. HAL_CAN_Man_kPWF = 11, + /// Studica. HAL_CAN_Man_kStudica = 12, + /// TheThriftyBot. HAL_CAN_Man_kTheThriftyBot = 13, + /// Redux Robotics. HAL_CAN_Man_kReduxRobotics = 14, + /// AndyMark. HAL_CAN_Man_kAndyMark = 15, + /// Vivid-Hosting. HAL_CAN_Man_kVividHosting = 16 }; /** @} */ diff --git a/hal/src/main/native/include/hal/DMA.h b/hal/src/main/native/include/hal/DMA.h index cfa2f8a870a..2d570a40320 100644 --- a/hal/src/main/native/include/hal/DMA.h +++ b/hal/src/main/native/include/hal/DMA.h @@ -138,6 +138,8 @@ void HAL_AddDMACounter(HAL_DMAHandle handle, HAL_CounterHandle counterHandle, /** * Adds timer data for an counter to be collected by DMA. * + * This can only be called if DMA is not started. + * * @param[in] handle the dma handle * @param[in] counterHandle the counter to add * @param[out] status Error status variable. 0 on success. diff --git a/hal/src/main/native/include/hal/HALBase.h b/hal/src/main/native/include/hal/HALBase.h index dd7bb704b91..3e9ddc67d1c 100644 --- a/hal/src/main/native/include/hal/HALBase.h +++ b/hal/src/main/native/include/hal/HALBase.h @@ -110,6 +110,10 @@ HAL_RuntimeType HAL_GetRuntimeType(void); /** * Gets the state of the "USER" button on the roboRIO. * + * @warning the User Button is used to stop user programs from automatically + * loading if it is held for more then 5 seconds. Because of this, it's not + * recommended to be used by teams for any other purpose. + * * @param[out] status the error code, or 0 for success * @return true if the button is currently pressed down */ diff --git a/hal/src/main/native/include/hal/PowerDistribution.h b/hal/src/main/native/include/hal/PowerDistribution.h index 701b64d9f5a..c2dc3cd705e 100644 --- a/hal/src/main/native/include/hal/PowerDistribution.h +++ b/hal/src/main/native/include/hal/PowerDistribution.h @@ -217,12 +217,21 @@ void HAL_SetPowerDistributionSwitchableChannel( HAL_Bool HAL_GetPowerDistributionSwitchableChannel( HAL_PowerDistributionHandle handle, int32_t* status); +/** + * Power distribution version. + */ struct HAL_PowerDistributionVersion { + /// Firmware major version number. uint32_t firmwareMajor; + /// Firmware minor version number. uint32_t firmwareMinor; + /// Firmware fix version number. uint32_t firmwareFix; + /// Hardware minor version number. uint32_t hardwareMinor; + /// Hardware major version number. uint32_t hardwareMajor; + /// Unique ID. uint32_t uniqueId; }; diff --git a/hal/src/main/native/sim/DriverStation.cpp b/hal/src/main/native/sim/DriverStation.cpp index 67b2eb728a4..b245eed59d5 100644 --- a/hal/src/main/native/sim/DriverStation.cpp +++ b/hal/src/main/native/sim/DriverStation.cpp @@ -382,6 +382,7 @@ void NewDriverStationData() { } lastGiven = given; + SimDriverStationData->dsAttached = true; driverStation->newDataEvents.Wakeup(); SimDriverStationData->CallNewDataCallbacks(); } diff --git a/hal/src/main/native/sim/mockdata/DriverStationData.cpp b/hal/src/main/native/sim/mockdata/DriverStationData.cpp index a0cae1c9a2c..d124b213080 100644 --- a/hal/src/main/native/sim/mockdata/DriverStationData.cpp +++ b/hal/src/main/native/sim/mockdata/DriverStationData.cpp @@ -28,7 +28,7 @@ void DriverStationData::ResetData() { test.Reset(false); eStop.Reset(false); fmsAttached.Reset(false); - dsAttached.Reset(true); + dsAttached.Reset(false); allianceStationId.Reset(static_cast(0)); matchTime.Reset(-1.0); diff --git a/hal/src/main/native/sim/mockdata/DriverStationDataInternal.h b/hal/src/main/native/sim/mockdata/DriverStationDataInternal.h index 763b465f328..008329fde86 100644 --- a/hal/src/main/native/sim/mockdata/DriverStationDataInternal.h +++ b/hal/src/main/native/sim/mockdata/DriverStationDataInternal.h @@ -122,7 +122,7 @@ class DriverStationData { SimDataValue eStop{false}; SimDataValue fmsAttached{ false}; - SimDataValue dsAttached{true}; + SimDataValue dsAttached{false}; SimDataValue allianceStationId{static_cast(0)}; diff --git a/ntcore/doc/alloy-model.adoc b/ntcore/doc/alloy-model.adoc index 35ca7d85adb..fb7b372b7e4 100644 --- a/ntcore/doc/alloy-model.adoc +++ b/ntcore/doc/alloy-model.adoc @@ -1,6 +1,6 @@ = Network Tables Alloy Model -Alloy (http://alloy.mit.edu/alloy/) is a formal logic tool that can analyze +[Alloy](https://www.csail.mit.edu/research/alloy) is a formal logic tool that can analyze first-order logic expressions. Under the proposed sequence number -based protocol, assuming that all nodes start from the same state, Alloy is unable to find a way where two nodes with the same sequence number have different state diff --git a/ntcore/src/generate/main/java/GenericPublisher.java.jinja b/ntcore/src/generate/main/java/GenericPublisher.java.jinja index f6d7001733e..5af0d3b3e25 100644 --- a/ntcore/src/generate/main/java/GenericPublisher.java.jinja +++ b/ntcore/src/generate/main/java/GenericPublisher.java.jinja @@ -236,6 +236,12 @@ public interface GenericPublisher extends Publisher, Consumer boolean setDefault{{ t.TypeName }}({{ t.java.ValueType }} defaultValue); {% endif -%} {% if t.java.WrapValueType %} + /** + * Sets the entry's value if it does not exist. + * + * @param defaultValue the default value to set + * @return False if the entry exists with a different type + */ boolean setDefault{{ t.TypeName }}({{ t.java.WrapValueType }} defaultValue); {% endif -%} {% endfor %} diff --git a/ntcore/src/generate/main/java/NetworkTableEntry.java.jinja b/ntcore/src/generate/main/java/NetworkTableEntry.java.jinja index 2c2c7373a8c..fa3a72a949a 100644 --- a/ntcore/src/generate/main/java/NetworkTableEntry.java.jinja +++ b/ntcore/src/generate/main/java/NetworkTableEntry.java.jinja @@ -608,6 +608,6 @@ public final class NetworkTableEntry implements Publisher, Subscriber { } private final Topic m_topic; - protected int m_handle; + private final int m_handle; } diff --git a/ntcore/src/generate/main/java/NetworkTableInstance.java.jinja b/ntcore/src/generate/main/java/NetworkTableInstance.java.jinja index 20861cd59b2..145193e2674 100644 --- a/ntcore/src/generate/main/java/NetworkTableInstance.java.jinja +++ b/ntcore/src/generate/main/java/NetworkTableInstance.java.jinja @@ -68,6 +68,11 @@ public final class NetworkTableInstance implements AutoCloseable { this.value = value; } + /** + * Returns the network mode value. + * + * @return The network mode value. + */ public int getValue() { return value; } diff --git a/ntcore/src/generate/main/java/NetworkTablesJNI.java.jinja b/ntcore/src/generate/main/java/NetworkTablesJNI.java.jinja index 17e95dd4836..5c43b44e7d1 100644 --- a/ntcore/src/generate/main/java/NetworkTablesJNI.java.jinja +++ b/ntcore/src/generate/main/java/NetworkTablesJNI.java.jinja @@ -14,20 +14,35 @@ import java.util.EnumSet; import java.util.OptionalLong; import java.util.concurrent.atomic.AtomicBoolean; +/** NetworkTables JNI. */ public final class NetworkTablesJNI { static boolean libraryLoaded = false; static RuntimeLoader 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 { @@ -68,139 +83,494 @@ public final class NetworkTablesJNI { return new PubSubOptions(options); } + /** + * Returns default instance handle. + * + * @return Default instance handle. + */ public static native int getDefaultInstance(); + /** + * Creates an NT instance. + * + * @return NT instance handle. + */ public static native int createInstance(); + /** + * Destroys an NT instance. + * + * @param inst NT instance handle. + */ public static native void destroyInstance(int inst); + /** + * Returns NT instance from handle. + * + * @param handle NT instance handle. + * @return NT instance. + */ public static native int getInstanceFromHandle(int handle); private static native int getEntryImpl( int topic, int type, String typeStr, PubSubOptions options); + /** + * Returns NT entry handle. + * + * @param inst NT instance handle. + * @param key NT entry key. + * @return NT entry handle. + */ public static native int getEntry(int inst, String key); + /** + * Returns NT entry handle. + * + * @param topic NT entry topic. + * @param type NT entry type. + * @param typeStr NT entry type as a string. + * @param options NT entry pubsub options. + * @return NT entry handle. + */ public static int getEntry( int topic, int type, String typeStr, PubSubOptions options) { return getEntryImpl(topic, type, typeStr, options); } + /** + * Returns NT entry handle. + * + * @param topic NT entry topic. + * @param type NT entry type. + * @param typeStr NT entry type as a string. + * @param options NT entry pubsub options. + * @return NT entry handle. + */ public static int getEntry( int topic, int type, String typeStr, PubSubOption... options) { return getEntryImpl(topic, type, typeStr, buildOptions(options)); } + /** + * Returns NT entry name. + * + * @param entry NT entry handle. + * @return NT entry name. + */ public static native String getEntryName(int entry); + /** + * Returns NT entry last change time in microseconds. + * + * @param entry NT entry handle. + * @return NT entry last change time in microseconds. + */ public static native long getEntryLastChange(int entry); + /** + * Returns NT entry type. + * + * @param entry NT entry handle. + * @return NT entry type. + */ public static native int getType(int entry); /* Topic functions */ + /** + * Returns list of topic handles. + * + * @param inst NT instance handle. + * @param prefix Topic prefix. + * @param types Topic types. + * @return List of topic handles. + */ public static native int[] getTopics(int inst, String prefix, int types); + /** + * Returns list of topic handles. + * + * @param inst NT instance handle. + * @param prefix Topic prefix. + * @param types Topic types as strings. + * @return List of topic handles. + */ public static native int[] getTopicsStr(int inst, String prefix, String[] types); + /** + * Returns list of topic infos. + * + * @param instObject NT instance. + * @param inst NT instance handle. + * @param prefix Topic prefix. + * @param types Topic types. + * @return List of topic infos. + */ public static native TopicInfo[] getTopicInfos( NetworkTableInstance instObject, int inst, String prefix, int types); + /** + * Returns list of topic infos. + * + * @param instObject NT instance. + * @param inst NT instance handle. + * @param prefix Topic prefix. + * @param types Topic types as strings. + * @return List of topic infos. + */ public static native TopicInfo[] getTopicInfosStr( NetworkTableInstance instObject, int inst, String prefix, String[] types); + /** + * Returns Topic handle. + * + * @param inst NT instance handle. + * @param name Topic name. + * @return Topic handle. + */ public static native int getTopic(int inst, String name); + /** + * Returns topic name. + * + * @param topic Topic handle. + * @return Topic name. + */ public static native String getTopicName(int topic); + /** + * Returns topic type. + * + * @param topic Topic handle. + * @return Topic type. + */ public static native int getTopicType(int topic); + /** + * Sets topic persistency. + * + * @param topic Topic handle. + * @param value True if topic should be persistent. + */ public static native void setTopicPersistent(int topic, boolean value); + /** + * Returns true if topic is persistent. + * + * @param topic Topic handle. + * @return True if topic is persistent. + */ public static native boolean getTopicPersistent(int topic); + /** + * Sets whether topic is retained. + * + * @param topic Topic handle. + * @param value True if topic should be retained. + */ public static native void setTopicRetained(int topic, boolean value); + /** + * Returns true if topic is retained. + * + * @param topic Topic handle. + * @return True if topic is retained. + */ public static native boolean getTopicRetained(int topic); + /** + * Sets topic caching. + * + * @param topic Topic handle. + * @param value True if topic should be cached. + */ public static native void setTopicCached(int topic, boolean value); + /** + * Returns true if topic is cached. + * + * @param topic Topic handle. + * @return True if topic is cached. + */ public static native boolean getTopicCached(int topic); + /** + * Returns topic type as string. + * + * @param topic Topic handle. + * @return Topic type as string. + */ public static native String getTopicTypeString(int topic); + /** + * Returns true if topic exists. + * + * @param topic Topic handle. + * @return True if topic exists. + */ public static native boolean getTopicExists(int topic); + /** + * Returns topic property. + * + * @param topic Topic handle. + * @param name Property name. + * @return Topic property. + */ public static native String getTopicProperty(int topic, String name); + /** + * Sets topic property. + * + * @param topic Topic handle. + * @param name Property name. + * @param value Property value. + */ public static native void setTopicProperty(int topic, String name, String value); + /** + * Deletes topic property. + * + * @param topic Topic handle. + * @param name Property name. + */ public static native void deleteTopicProperty(int topic, String name); + /** + * Returns topic properties. + * + * @param topic Topic handle. + * @return Topic properties. + */ public static native String getTopicProperties(int topic); + /** + * Sets topic properties. + * + * @param topic Topic handle. + * @param properties Topic properties. + */ public static native void setTopicProperties(int topic, String properties); + /** + * Subscribes to topic. + * + * @param topic Topic handle. + * @param type Topic type. + * @param typeStr Topic type as a string. + * @param options Pubsub options. + * @return Subscriber handle. + */ public static native int subscribe( int topic, int type, String typeStr, PubSubOptions options); + /** + * Subscribes to topic. + * + * @param topic Topic handle. + * @param type Topic type. + * @param typeStr Topic type as a string. + * @param options Pubsub options. + * @return Subscriber handle. + */ public static int subscribe( int topic, int type, String typeStr, PubSubOption... options) { return subscribe(topic, type, typeStr, buildOptions(options)); } + /** + * Unsubscribes from topic. + * + * @param sub Subscriber handle. + */ public static native void unsubscribe(int sub); + /** + * Publishes topic. + * + * @param topic Topic handle. + * @param type Topic type. + * @param typeStr Topic type as a string. + * @param options Pubsub options. + * @return Publish handle. + */ public static native int publish( int topic, int type, String typeStr, PubSubOptions options); + /** + * Publishes topic. + * + * @param topic Topic handle. + * @param type Topic type. + * @param typeStr Topic type as a string. + * @param options Pubsub options. + * @return Publish handle. + */ public static int publish( int topic, int type, String typeStr, PubSubOption... options) { return publish(topic, type, typeStr, buildOptions(options)); } + /** + * Publishes topic. + * + * @param topic Topic handle. + * @param type Topic type. + * @param typeStr Topic type as a string. + * @param properties Topic properties. + * @param options Pubsub options. + * @return Publish handle. + */ public static native int publishEx( int topic, int type, String typeStr, String properties, PubSubOptions options); + /** + * Publishes topic. + * + * @param topic Topic handle. + * @param type Topic type. + * @param typeStr Topic type as a string. + * @param properties Topic properties. + * @param options Pubsub options. + * @return Publish handle. + */ public static int publishEx( int topic, int type, String typeStr, String properties, PubSubOption... options) { return publishEx(topic, type, typeStr, properties, buildOptions(options)); } + /** + * Unpublishes topic. + * + * @param pubentry Publish entry handle. + */ public static native void unpublish(int pubentry); + /** + * Releases NT entry. + * + * @param entry NT entry handle. + */ public static native void releaseEntry(int entry); + /** + * Relesaes pubsub entry. + * + * @param pubsubentry Pubsub entry handle. + */ public static native void release(int pubsubentry); + /** + * Returns topic from pubsub entry handle. + * + * @param pubsubentry Pubsub entry handle. + * @return Topic handle. + */ public static native int getTopicFromHandle(int pubsubentry); + /** + * Subscribes to multiple topics. + * + * @param inst NT instance handle. + * @param prefixes List of topic prefixes. + * @param options Pubsub options. + * @return Subscribe handle. + */ public static native int subscribeMultiple(int inst, String[] prefixes, PubSubOptions options); + /** + * Subscribes to multiple topics. + * + * @param inst NT instance handle. + * @param prefixes List of topic prefixes. + * @param options Pubsub options. + * @return Subscribe handle. + */ public static int subscribeMultiple(int inst, String[] prefixes, PubSubOption... options) { return subscribeMultiple(inst, prefixes, buildOptions(options)); } + /** + * Unsubscribes from multiple topics. + * + * @param sub Subscribe handle. + */ public static native void unsubscribeMultiple(int sub); {% for t in types %} + /** + * Returns timestamped topic value as an atomic {{ t.TypeName }}. + * + * @param subentry Subentry handle. + * @param defaultValue Default value. + * @return Timestamped topic value. + */ public static native Timestamped{{ t.TypeName }} getAtomic{{ t.TypeName }}( int subentry, {{ t.java.ValueType }} defaultValue); + /** + * Returns queued timestamped topic values. + * + * @param subentry Subentry handle. + * @return List of timestamped topic values. + */ public static native Timestamped{{ t.TypeName }}[] readQueue{{ t.TypeName }}(int subentry); + /** + * Returns queued topic values. + * + * @param subentry Subentry handle. + * @return List of topic values. + */ public static native {{ t.java.ValueType }}[] readQueueValues{{ t.TypeName }}(int subentry); {% if t.TypeName == "Raw" %} + /** + * Sets raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Raw value buffer. + * @return True if set succeeded. + */ public static boolean setRaw(int entry, long time, byte[] value) { return setRaw(entry, time, value, 0, value.length); } + /** + * Sets raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Raw value buffer. + * @param start Value's offset into buffer. + * @param len Length of value in buffer. + * @return True if set succeeded. + */ public static native boolean setRaw(int entry, long time, byte[] value, int start, int len); + /** + * Sets raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Raw value buffer. + * @return True if set succeeded. + */ public static boolean setRaw(int entry, long time, ByteBuffer value) { int pos = value.position(); return setRaw(entry, time, value, pos, value.capacity() - pos); } + /** + * Sets raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Raw value buffer. + * @param start Value's offset into buffer. + * @param len Length of value in buffer. + * @return True if set succeeded. + */ public static boolean setRaw(int entry, long time, ByteBuffer value, int start, int len) { if (value.isDirect()) { if (start < 0) { @@ -220,23 +590,84 @@ public final class NetworkTablesJNI { } } + /** + * Sets raw topic value buffer. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Raw value buffer. + * @param start Value's offset into buffer. + * @param len Length of value in buffer. + * @return True if set succeeded. + */ private static native boolean setRawBuffer(int entry, long time, ByteBuffer value, int start, int len); {% else %} + /** + * Sets topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Topic value. + * @return True if set succeeded. + */ public static native boolean set{{ t.TypeName }}(int entry, long time, {{ t.java.ValueType }} value); {% endif %} + /** + * Returns topic value. + * + * @param entry Entry handle. + * @param defaultValue Default value. + * @return Topic value. + */ public static native {{ t.java.ValueType }} get{{ t.TypeName }}(int entry, {{ t.java.ValueType }} defaultValue); {% if t.TypeName == "Raw" %} + /** + * Sets default raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static boolean setDefaultRaw(int entry, long time, byte[] defaultValue) { return setDefaultRaw(entry, time, defaultValue, 0, defaultValue.length); } + /** + * Sets default raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @param start Value's offset into buffer. + * @param len Length of value in buffer. + * @return True if set succeeded. + */ public static native boolean setDefaultRaw(int entry, long time, byte[] defaultValue, int start, int len); + /** + * Sets default raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static boolean setDefaultRaw(int entry, long time, ByteBuffer defaultValue) { int pos = defaultValue.position(); return setDefaultRaw(entry, time, defaultValue, pos, defaultValue.limit() - pos); } + /** + * Sets default raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @param start Value's offset into buffer. + * @param len Length of value in buffer. + * @return True if set succeeded. + */ public static boolean setDefaultRaw(int entry, long time, ByteBuffer defaultValue, int start, int len) { if (defaultValue.isDirect()) { if (start < 0) { @@ -256,25 +687,91 @@ public final class NetworkTablesJNI { } } + /** + * Sets default raw topic value buffer. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @param start Value's offset into buffer. + * @param len Length of value in buffer. + * @return True if set succeeded. + */ private static native boolean setDefaultRawBuffer(int entry, long time, ByteBuffer defaultValue, int start, int len); {% else %} + /** + * Sets default topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static native boolean setDefault{{ t.TypeName }}(int entry, long time, {{ t.java.ValueType }} defaultValue); {% endif %} {% endfor %} + /** + * Returns queued subentry values. + * + * @param subentry Subentry handle. + * @return List of queued subentry values. + */ public static native NetworkTableValue[] readQueueValue(int subentry); + /** + * Returns entry's NT value. + * + * @param entry Entry handle. + * @return Entry's NT value. + */ public static native NetworkTableValue getValue(int entry); + /** + * Sets entry flags. + * + * @param entry Entry handle. + * @param flags Entry flags. + */ public static native void setEntryFlags(int entry, int flags); + /** + * Returns entry flags. + * + * @param entry Entry handle. + * @return Entry flags. + */ public static native int getEntryFlags(int entry); + /** + * Returns topic info. + * + * @param inst NT instance handle. + * @param topic Topic handle. + * @return Topic info. + */ public static native TopicInfo getTopicInfo(NetworkTableInstance inst, int topic); + /** + * Creates a listener poller. + * + * @param inst NT instance handle. + * @return Listener poller handle. + */ public static native int createListenerPoller(int inst); + /** + * Destroys listener poller. + * + * @param poller Listener poller handle. + */ public static native void destroyListenerPoller(int poller); + /** + * Converts NT event kinds to mask. + * + * @param kinds Enum set of NT event kinds. + * @return NT event mask. + */ private static int kindsToMask(EnumSet kinds) { int mask = 0; for (NetworkTableEvent.Kind kind : kinds) { @@ -283,80 +780,319 @@ public final class NetworkTablesJNI { return mask; } + /** + * Adds listener. + * + * @param poller Listener poller handle. + * @param prefixes Topic prefixes. + * @param kinds Enum set of NT event kinds. + * @return Listener handle. + */ public static int addListener(int poller, String[] prefixes, EnumSet kinds) { return addListener(poller, prefixes, kindsToMask(kinds)); } + /** + * Adds listener. + * + * @param poller Listener poller handle. + * @param handle Topic handle. + * @param kinds Enum set of NT event kinds. + * @return Listener handle. + */ public static int addListener(int poller, int handle, EnumSet kinds) { return addListener(poller, handle, kindsToMask(kinds)); } + /** + * Adds listener. + * + * @param poller Listener poller handle. + * @param prefixes Topic prefixes. + * @param mask NT event mask. + * @return Listener handle. + */ public static native int addListener(int poller, String[] prefixes, int mask); + /** + * Adds listener. + * + * @param poller Listener poller handle. + * @param handle Topic handle. + * @param mask NT event mask. + * @return Listener handle. + */ public static native int addListener(int poller, int handle, int mask); + /** + * Returns NT events from listener queue. + * + * @param inst NT instance handle. + * @param poller Listener poller handle. + * @return List of NT events. + */ public static native NetworkTableEvent[] readListenerQueue( NetworkTableInstance inst, int poller); + /** + * Removes listener. + * + * @param listener Listener handle. + */ public static native void removeListener(int listener); + /** + * Returns network mode. + * + * @param inst NT instance handle. + * @return Network mode. + */ public static native int getNetworkMode(int inst); + /** + * Starts local-only operation. Prevents calls to startServer or startClient from taking effect. + * Has no effect if startServer or startClient has already been called. + * + * @param inst NT instance handle. + */ public static native void startLocal(int inst); + /** + * Stops local-only operation. startServer or startClient can be called after this call to start + * a server or client. + * + * @param inst NT instance handle. + */ public static native void stopLocal(int inst); + /** + * Starts a server using the specified filename, listening address, and port. + * + * @param inst NT instance handle. + * @param persistFilename the name of the persist file to use + * @param listenAddress the address to listen on, or empty to listen on any address + * @param port3 port to communicate over (NT3) + * @param port4 port to communicate over (NT4) + */ public static native void startServer( int inst, String persistFilename, String listenAddress, int port3, int port4); + /** + * Stops the server if it is running. + * + * @param inst NT instance handle. + */ public static native void stopServer(int inst); + /** + * Starts a NT3 client. Use SetServer or SetServerTeam to set the server name and port. + * + * @param inst NT instance handle. + * @param identity network identity to advertise (cannot be empty string) + */ public static native void startClient3(int inst, String identity); + /** + * Starts a NT4 client. Use SetServer or SetServerTeam to set the server name and port. + * + * @param inst NT instance handle. + * @param identity network identity to advertise (cannot be empty string) + */ public static native void startClient4(int inst, String identity); + /** + * Stops the client if it is running. + * + * @param inst NT instance handle. + */ public static native void stopClient(int inst); + /** + * Sets server address and port for client (without restarting client). + * + * @param inst NT instance handle. + * @param serverName server name + * @param port port to communicate over + */ public static native void setServer(int inst, String serverName, int port); + /** + * Sets server addresses and ports for client (without restarting client). The client will + * attempt to connect to each server in round robin fashion. + * + * @param inst NT instance handle. + * @param serverNames array of server names + * @param ports array of port numbers (0=default) + */ public static native void setServer(int inst, String[] serverNames, int[] ports); + /** + * Sets server addresses and port for client (without restarting client). Connects using commonly + * known robot addresses for the specified team. + * + * @param inst NT instance handle. + * @param team team number + * @param port port to communicate over + */ public static native void setServerTeam(int inst, int team, int port); + /** + * Disconnects the client if it's running and connected. This will automatically start + * reconnection attempts to the current server list. + * + * @param inst NT instance handle. + */ public static native void disconnect(int inst); + /** + * Starts requesting server address from Driver Station. This connects to the Driver Station + * running on localhost to obtain the server IP address. + * + * @param inst NT instance handle. + * @param port server port to use in combination with IP from DS + */ public static native void startDSClient(int inst, int port); + /** + * Stops requesting server address from Driver Station. + * + * @param inst NT instance handle. + */ public static native void stopDSClient(int inst); + /** + * Flushes all updated values immediately to the local client/server. This does not flush to the + * network. + * + * @param inst NT instance handle. + */ public static native void flushLocal(int inst); + /** + * Flushes all updated values immediately to the network. Note: This is rate-limited to protect + * the network from flooding. This is primarily useful for synchronizing network updates with + * user code. + * + * @param inst NT instance handle. + */ public static native void flush(int inst); + /** + * Gets information on the currently established network connections. If operating as a client, + * this will return either zero or one values. + * + * @param inst NT instance handle. + * @return array of connection information + */ public static native ConnectionInfo[] getConnections(int inst); + /** + * Return whether or not the instance is connected to another node. + * + * @param inst NT instance handle. + * @return True if connected. + */ public static native boolean isConnected(int inst); + /** + * Get the time offset between server time and local time. Add this value to local time to get + * the estimated equivalent server time. In server mode, this always returns 0. In client mode, + * this returns the time offset only if the client and server are connected and have exchanged + * synchronization messages. Note the time offset may change over time as it is periodically + * updated; to receive updates as events, add a listener to the "time sync" event. + * + * @param inst NT instance handle. + * @return Time offset in microseconds (optional) + */ public static native OptionalLong getServerTimeOffset(int inst); + /** + * Returns the current timestamp in microseconds. + * + * @return The current timestsamp in microseconds. + */ public static native long now(); + /** + * Starts logging entry changes to a DataLog. + * + * @param inst NT instance handle. + * @param log data log handle; lifetime must extend until StopEntryDataLog is called or the + * instance is destroyed + * @param prefix only store entries with names that start with this prefix; the prefix is not + * included in the data log entry name + * @param logPrefix prefix to add to data log entry names + * @return Data logger handle + */ private static native int startEntryDataLog(int inst, long log, String prefix, String logPrefix); + /** + * Starts logging entry changes to a DataLog. + * + * @param inst NT instance handle. + * @param log data log object; lifetime must extend until StopEntryDataLog is called or the + * instance is destroyed + * @param prefix only store entries with names that start with this prefix; the prefix is not + * included in the data log entry name + * @param logPrefix prefix to add to data log entry names + * @return Data logger handle + */ public static int startEntryDataLog(int inst, DataLog log, String prefix, String logPrefix) { return startEntryDataLog(inst, log.getImpl(), prefix, logPrefix); } + /** + * Stops logging entry changes to a DataLog. + * + * @param logger data logger handle + */ public static native void stopEntryDataLog(int logger); + /** + * Starts logging connection changes to a DataLog. + * + * @param inst NT instance handle. + * @param log data log handle; lifetime must extend until StopConnectionDataLog is called or the + * instance is destroyed + * @param name data log entry name + * @return Data logger handle + */ private static native int startConnectionDataLog(int inst, long log, String name); + /** + * Starts logging connection changes to a DataLog. + * + * @param inst NT instance handle. + * @param log data log object; lifetime must extend until StopConnectionDataLog is called or the + * instance is destroyed + * @param name data log entry name + * @return Data logger handle + */ public static int startConnectionDataLog(int inst, DataLog log, String name) { return startConnectionDataLog(inst, log.getImpl(), name); } + /** + * Stops logging connection changes to a DataLog. + * + * @param logger data logger handle + */ public static native void stopConnectionDataLog(int logger); + /** + * Add logger callback function. By default, log messages are sent to stderr; this function sends + * log messages with the specified levels to the provided callback function instead. The callback + * function will only be called for log messages with level greater than or equal to minLevel and + * less than or equal to maxLevel; messages outside this range will be silently ignored. + * + * @param poller Listener poller handle. + * @param minLevel minimum log level + * @param maxLevel maximum log level + * @return Listener handle + */ public static native int addLogger(int poller, int minLevel, int maxLevel); + + /** Utility class. */ + private NetworkTablesJNI() {} } diff --git a/ntcore/src/generated/main/java/edu/wpi/first/networktables/GenericPublisher.java b/ntcore/src/generated/main/java/edu/wpi/first/networktables/GenericPublisher.java index 8fd03ec330c..f589e9f5497 100644 --- a/ntcore/src/generated/main/java/edu/wpi/first/networktables/GenericPublisher.java +++ b/ntcore/src/generated/main/java/edu/wpi/first/networktables/GenericPublisher.java @@ -517,6 +517,12 @@ default boolean setDefaultRaw(ByteBuffer defaultValue) { */ boolean setDefaultBooleanArray(boolean[] defaultValue); + /** + * Sets the entry's value if it does not exist. + * + * @param defaultValue the default value to set + * @return False if the entry exists with a different type + */ boolean setDefaultBooleanArray(Boolean[] defaultValue); @@ -528,6 +534,12 @@ default boolean setDefaultRaw(ByteBuffer defaultValue) { */ boolean setDefaultIntegerArray(long[] defaultValue); + /** + * Sets the entry's value if it does not exist. + * + * @param defaultValue the default value to set + * @return False if the entry exists with a different type + */ boolean setDefaultIntegerArray(Long[] defaultValue); @@ -539,6 +551,12 @@ default boolean setDefaultRaw(ByteBuffer defaultValue) { */ boolean setDefaultFloatArray(float[] defaultValue); + /** + * Sets the entry's value if it does not exist. + * + * @param defaultValue the default value to set + * @return False if the entry exists with a different type + */ boolean setDefaultFloatArray(Float[] defaultValue); @@ -550,6 +568,12 @@ default boolean setDefaultRaw(ByteBuffer defaultValue) { */ boolean setDefaultDoubleArray(double[] defaultValue); + /** + * Sets the entry's value if it does not exist. + * + * @param defaultValue the default value to set + * @return False if the entry exists with a different type + */ boolean setDefaultDoubleArray(Double[] defaultValue); diff --git a/ntcore/src/generated/main/java/edu/wpi/first/networktables/NetworkTableEntry.java b/ntcore/src/generated/main/java/edu/wpi/first/networktables/NetworkTableEntry.java index 69d107753a0..bec010bf238 100644 --- a/ntcore/src/generated/main/java/edu/wpi/first/networktables/NetworkTableEntry.java +++ b/ntcore/src/generated/main/java/edu/wpi/first/networktables/NetworkTableEntry.java @@ -1009,5 +1009,5 @@ public int hashCode() { } private final Topic m_topic; - protected int m_handle; + private final int m_handle; } diff --git a/ntcore/src/generated/main/java/edu/wpi/first/networktables/NetworkTableInstance.java b/ntcore/src/generated/main/java/edu/wpi/first/networktables/NetworkTableInstance.java index e5872ba425f..344e061a828 100644 --- a/ntcore/src/generated/main/java/edu/wpi/first/networktables/NetworkTableInstance.java +++ b/ntcore/src/generated/main/java/edu/wpi/first/networktables/NetworkTableInstance.java @@ -68,6 +68,11 @@ public enum NetworkMode { this.value = value; } + /** + * Returns the network mode value. + * + * @return The network mode value. + */ public int getValue() { return value; } diff --git a/ntcore/src/generated/main/java/edu/wpi/first/networktables/NetworkTablesJNI.java b/ntcore/src/generated/main/java/edu/wpi/first/networktables/NetworkTablesJNI.java index b65b8d97342..4b4fa5cb6ed 100644 --- a/ntcore/src/generated/main/java/edu/wpi/first/networktables/NetworkTablesJNI.java +++ b/ntcore/src/generated/main/java/edu/wpi/first/networktables/NetworkTablesJNI.java @@ -14,20 +14,35 @@ import java.util.OptionalLong; import java.util.concurrent.atomic.AtomicBoolean; +/** NetworkTables JNI. */ public final class NetworkTablesJNI { static boolean libraryLoaded = false; static RuntimeLoader 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 { @@ -68,209 +83,774 @@ private static PubSubOptions buildOptions(PubSubOption... options) { return new PubSubOptions(options); } + /** + * Returns default instance handle. + * + * @return Default instance handle. + */ public static native int getDefaultInstance(); + /** + * Creates an NT instance. + * + * @return NT instance handle. + */ public static native int createInstance(); + /** + * Destroys an NT instance. + * + * @param inst NT instance handle. + */ public static native void destroyInstance(int inst); + /** + * Returns NT instance from handle. + * + * @param handle NT instance handle. + * @return NT instance. + */ public static native int getInstanceFromHandle(int handle); private static native int getEntryImpl( int topic, int type, String typeStr, PubSubOptions options); + /** + * Returns NT entry handle. + * + * @param inst NT instance handle. + * @param key NT entry key. + * @return NT entry handle. + */ public static native int getEntry(int inst, String key); + /** + * Returns NT entry handle. + * + * @param topic NT entry topic. + * @param type NT entry type. + * @param typeStr NT entry type as a string. + * @param options NT entry pubsub options. + * @return NT entry handle. + */ public static int getEntry( int topic, int type, String typeStr, PubSubOptions options) { return getEntryImpl(topic, type, typeStr, options); } + /** + * Returns NT entry handle. + * + * @param topic NT entry topic. + * @param type NT entry type. + * @param typeStr NT entry type as a string. + * @param options NT entry pubsub options. + * @return NT entry handle. + */ public static int getEntry( int topic, int type, String typeStr, PubSubOption... options) { return getEntryImpl(topic, type, typeStr, buildOptions(options)); } + /** + * Returns NT entry name. + * + * @param entry NT entry handle. + * @return NT entry name. + */ public static native String getEntryName(int entry); + /** + * Returns NT entry last change time in microseconds. + * + * @param entry NT entry handle. + * @return NT entry last change time in microseconds. + */ public static native long getEntryLastChange(int entry); + /** + * Returns NT entry type. + * + * @param entry NT entry handle. + * @return NT entry type. + */ public static native int getType(int entry); /* Topic functions */ + /** + * Returns list of topic handles. + * + * @param inst NT instance handle. + * @param prefix Topic prefix. + * @param types Topic types. + * @return List of topic handles. + */ public static native int[] getTopics(int inst, String prefix, int types); + /** + * Returns list of topic handles. + * + * @param inst NT instance handle. + * @param prefix Topic prefix. + * @param types Topic types as strings. + * @return List of topic handles. + */ public static native int[] getTopicsStr(int inst, String prefix, String[] types); + /** + * Returns list of topic infos. + * + * @param instObject NT instance. + * @param inst NT instance handle. + * @param prefix Topic prefix. + * @param types Topic types. + * @return List of topic infos. + */ public static native TopicInfo[] getTopicInfos( NetworkTableInstance instObject, int inst, String prefix, int types); + /** + * Returns list of topic infos. + * + * @param instObject NT instance. + * @param inst NT instance handle. + * @param prefix Topic prefix. + * @param types Topic types as strings. + * @return List of topic infos. + */ public static native TopicInfo[] getTopicInfosStr( NetworkTableInstance instObject, int inst, String prefix, String[] types); + /** + * Returns Topic handle. + * + * @param inst NT instance handle. + * @param name Topic name. + * @return Topic handle. + */ public static native int getTopic(int inst, String name); + /** + * Returns topic name. + * + * @param topic Topic handle. + * @return Topic name. + */ public static native String getTopicName(int topic); + /** + * Returns topic type. + * + * @param topic Topic handle. + * @return Topic type. + */ public static native int getTopicType(int topic); + /** + * Sets topic persistency. + * + * @param topic Topic handle. + * @param value True if topic should be persistent. + */ public static native void setTopicPersistent(int topic, boolean value); + /** + * Returns true if topic is persistent. + * + * @param topic Topic handle. + * @return True if topic is persistent. + */ public static native boolean getTopicPersistent(int topic); + /** + * Sets whether topic is retained. + * + * @param topic Topic handle. + * @param value True if topic should be retained. + */ public static native void setTopicRetained(int topic, boolean value); + /** + * Returns true if topic is retained. + * + * @param topic Topic handle. + * @return True if topic is retained. + */ public static native boolean getTopicRetained(int topic); + /** + * Sets topic caching. + * + * @param topic Topic handle. + * @param value True if topic should be cached. + */ public static native void setTopicCached(int topic, boolean value); + /** + * Returns true if topic is cached. + * + * @param topic Topic handle. + * @return True if topic is cached. + */ public static native boolean getTopicCached(int topic); + /** + * Returns topic type as string. + * + * @param topic Topic handle. + * @return Topic type as string. + */ public static native String getTopicTypeString(int topic); + /** + * Returns true if topic exists. + * + * @param topic Topic handle. + * @return True if topic exists. + */ public static native boolean getTopicExists(int topic); + /** + * Returns topic property. + * + * @param topic Topic handle. + * @param name Property name. + * @return Topic property. + */ public static native String getTopicProperty(int topic, String name); + /** + * Sets topic property. + * + * @param topic Topic handle. + * @param name Property name. + * @param value Property value. + */ public static native void setTopicProperty(int topic, String name, String value); + /** + * Deletes topic property. + * + * @param topic Topic handle. + * @param name Property name. + */ public static native void deleteTopicProperty(int topic, String name); + /** + * Returns topic properties. + * + * @param topic Topic handle. + * @return Topic properties. + */ public static native String getTopicProperties(int topic); + /** + * Sets topic properties. + * + * @param topic Topic handle. + * @param properties Topic properties. + */ public static native void setTopicProperties(int topic, String properties); + /** + * Subscribes to topic. + * + * @param topic Topic handle. + * @param type Topic type. + * @param typeStr Topic type as a string. + * @param options Pubsub options. + * @return Subscriber handle. + */ public static native int subscribe( int topic, int type, String typeStr, PubSubOptions options); + /** + * Subscribes to topic. + * + * @param topic Topic handle. + * @param type Topic type. + * @param typeStr Topic type as a string. + * @param options Pubsub options. + * @return Subscriber handle. + */ public static int subscribe( int topic, int type, String typeStr, PubSubOption... options) { return subscribe(topic, type, typeStr, buildOptions(options)); } + /** + * Unsubscribes from topic. + * + * @param sub Subscriber handle. + */ public static native void unsubscribe(int sub); + /** + * Publishes topic. + * + * @param topic Topic handle. + * @param type Topic type. + * @param typeStr Topic type as a string. + * @param options Pubsub options. + * @return Publish handle. + */ public static native int publish( int topic, int type, String typeStr, PubSubOptions options); + /** + * Publishes topic. + * + * @param topic Topic handle. + * @param type Topic type. + * @param typeStr Topic type as a string. + * @param options Pubsub options. + * @return Publish handle. + */ public static int publish( int topic, int type, String typeStr, PubSubOption... options) { return publish(topic, type, typeStr, buildOptions(options)); } + /** + * Publishes topic. + * + * @param topic Topic handle. + * @param type Topic type. + * @param typeStr Topic type as a string. + * @param properties Topic properties. + * @param options Pubsub options. + * @return Publish handle. + */ public static native int publishEx( int topic, int type, String typeStr, String properties, PubSubOptions options); + /** + * Publishes topic. + * + * @param topic Topic handle. + * @param type Topic type. + * @param typeStr Topic type as a string. + * @param properties Topic properties. + * @param options Pubsub options. + * @return Publish handle. + */ public static int publishEx( int topic, int type, String typeStr, String properties, PubSubOption... options) { return publishEx(topic, type, typeStr, properties, buildOptions(options)); } + /** + * Unpublishes topic. + * + * @param pubentry Publish entry handle. + */ public static native void unpublish(int pubentry); + /** + * Releases NT entry. + * + * @param entry NT entry handle. + */ public static native void releaseEntry(int entry); + /** + * Relesaes pubsub entry. + * + * @param pubsubentry Pubsub entry handle. + */ public static native void release(int pubsubentry); + /** + * Returns topic from pubsub entry handle. + * + * @param pubsubentry Pubsub entry handle. + * @return Topic handle. + */ public static native int getTopicFromHandle(int pubsubentry); + /** + * Subscribes to multiple topics. + * + * @param inst NT instance handle. + * @param prefixes List of topic prefixes. + * @param options Pubsub options. + * @return Subscribe handle. + */ public static native int subscribeMultiple(int inst, String[] prefixes, PubSubOptions options); + /** + * Subscribes to multiple topics. + * + * @param inst NT instance handle. + * @param prefixes List of topic prefixes. + * @param options Pubsub options. + * @return Subscribe handle. + */ public static int subscribeMultiple(int inst, String[] prefixes, PubSubOption... options) { return subscribeMultiple(inst, prefixes, buildOptions(options)); } + /** + * Unsubscribes from multiple topics. + * + * @param sub Subscribe handle. + */ public static native void unsubscribeMultiple(int sub); + /** + * Returns timestamped topic value as an atomic Boolean. + * + * @param subentry Subentry handle. + * @param defaultValue Default value. + * @return Timestamped topic value. + */ public static native TimestampedBoolean getAtomicBoolean( int subentry, boolean defaultValue); + /** + * Returns queued timestamped topic values. + * + * @param subentry Subentry handle. + * @return List of timestamped topic values. + */ public static native TimestampedBoolean[] readQueueBoolean(int subentry); + /** + * Returns queued topic values. + * + * @param subentry Subentry handle. + * @return List of topic values. + */ public static native boolean[] readQueueValuesBoolean(int subentry); + /** + * Sets topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Topic value. + * @return True if set succeeded. + */ public static native boolean setBoolean(int entry, long time, boolean value); + /** + * Returns topic value. + * + * @param entry Entry handle. + * @param defaultValue Default value. + * @return Topic value. + */ public static native boolean getBoolean(int entry, boolean defaultValue); + /** + * Sets default topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static native boolean setDefaultBoolean(int entry, long time, boolean defaultValue); + /** + * Returns timestamped topic value as an atomic Integer. + * + * @param subentry Subentry handle. + * @param defaultValue Default value. + * @return Timestamped topic value. + */ public static native TimestampedInteger getAtomicInteger( int subentry, long defaultValue); + /** + * Returns queued timestamped topic values. + * + * @param subentry Subentry handle. + * @return List of timestamped topic values. + */ public static native TimestampedInteger[] readQueueInteger(int subentry); + /** + * Returns queued topic values. + * + * @param subentry Subentry handle. + * @return List of topic values. + */ public static native long[] readQueueValuesInteger(int subentry); + /** + * Sets topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Topic value. + * @return True if set succeeded. + */ public static native boolean setInteger(int entry, long time, long value); + /** + * Returns topic value. + * + * @param entry Entry handle. + * @param defaultValue Default value. + * @return Topic value. + */ public static native long getInteger(int entry, long defaultValue); + /** + * Sets default topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static native boolean setDefaultInteger(int entry, long time, long defaultValue); + /** + * Returns timestamped topic value as an atomic Float. + * + * @param subentry Subentry handle. + * @param defaultValue Default value. + * @return Timestamped topic value. + */ public static native TimestampedFloat getAtomicFloat( int subentry, float defaultValue); + /** + * Returns queued timestamped topic values. + * + * @param subentry Subentry handle. + * @return List of timestamped topic values. + */ public static native TimestampedFloat[] readQueueFloat(int subentry); + /** + * Returns queued topic values. + * + * @param subentry Subentry handle. + * @return List of topic values. + */ public static native float[] readQueueValuesFloat(int subentry); + /** + * Sets topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Topic value. + * @return True if set succeeded. + */ public static native boolean setFloat(int entry, long time, float value); + /** + * Returns topic value. + * + * @param entry Entry handle. + * @param defaultValue Default value. + * @return Topic value. + */ public static native float getFloat(int entry, float defaultValue); + /** + * Sets default topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static native boolean setDefaultFloat(int entry, long time, float defaultValue); + /** + * Returns timestamped topic value as an atomic Double. + * + * @param subentry Subentry handle. + * @param defaultValue Default value. + * @return Timestamped topic value. + */ public static native TimestampedDouble getAtomicDouble( int subentry, double defaultValue); + /** + * Returns queued timestamped topic values. + * + * @param subentry Subentry handle. + * @return List of timestamped topic values. + */ public static native TimestampedDouble[] readQueueDouble(int subentry); + /** + * Returns queued topic values. + * + * @param subentry Subentry handle. + * @return List of topic values. + */ public static native double[] readQueueValuesDouble(int subentry); + /** + * Sets topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Topic value. + * @return True if set succeeded. + */ public static native boolean setDouble(int entry, long time, double value); + /** + * Returns topic value. + * + * @param entry Entry handle. + * @param defaultValue Default value. + * @return Topic value. + */ public static native double getDouble(int entry, double defaultValue); + /** + * Sets default topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static native boolean setDefaultDouble(int entry, long time, double defaultValue); + /** + * Returns timestamped topic value as an atomic String. + * + * @param subentry Subentry handle. + * @param defaultValue Default value. + * @return Timestamped topic value. + */ public static native TimestampedString getAtomicString( int subentry, String defaultValue); + /** + * Returns queued timestamped topic values. + * + * @param subentry Subentry handle. + * @return List of timestamped topic values. + */ public static native TimestampedString[] readQueueString(int subentry); + /** + * Returns queued topic values. + * + * @param subentry Subentry handle. + * @return List of topic values. + */ public static native String[] readQueueValuesString(int subentry); + /** + * Sets topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Topic value. + * @return True if set succeeded. + */ public static native boolean setString(int entry, long time, String value); + /** + * Returns topic value. + * + * @param entry Entry handle. + * @param defaultValue Default value. + * @return Topic value. + */ public static native String getString(int entry, String defaultValue); + /** + * Sets default topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static native boolean setDefaultString(int entry, long time, String defaultValue); + /** + * Returns timestamped topic value as an atomic Raw. + * + * @param subentry Subentry handle. + * @param defaultValue Default value. + * @return Timestamped topic value. + */ public static native TimestampedRaw getAtomicRaw( int subentry, byte[] defaultValue); + /** + * Returns queued timestamped topic values. + * + * @param subentry Subentry handle. + * @return List of timestamped topic values. + */ public static native TimestampedRaw[] readQueueRaw(int subentry); + /** + * Returns queued topic values. + * + * @param subentry Subentry handle. + * @return List of topic values. + */ public static native byte[][] readQueueValuesRaw(int subentry); + /** + * Sets raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Raw value buffer. + * @return True if set succeeded. + */ public static boolean setRaw(int entry, long time, byte[] value) { return setRaw(entry, time, value, 0, value.length); } + /** + * Sets raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Raw value buffer. + * @param start Value's offset into buffer. + * @param len Length of value in buffer. + * @return True if set succeeded. + */ public static native boolean setRaw(int entry, long time, byte[] value, int start, int len); + /** + * Sets raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Raw value buffer. + * @return True if set succeeded. + */ public static boolean setRaw(int entry, long time, ByteBuffer value) { int pos = value.position(); return setRaw(entry, time, value, pos, value.capacity() - pos); } + /** + * Sets raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Raw value buffer. + * @param start Value's offset into buffer. + * @param len Length of value in buffer. + * @return True if set succeeded. + */ public static boolean setRaw(int entry, long time, ByteBuffer value, int start, int len) { if (value.isDirect()) { if (start < 0) { @@ -290,21 +870,74 @@ public static boolean setRaw(int entry, long time, ByteBuffer value, int start, } } + /** + * Sets raw topic value buffer. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Raw value buffer. + * @param start Value's offset into buffer. + * @param len Length of value in buffer. + * @return True if set succeeded. + */ private static native boolean setRawBuffer(int entry, long time, ByteBuffer value, int start, int len); + /** + * Returns topic value. + * + * @param entry Entry handle. + * @param defaultValue Default value. + * @return Topic value. + */ public static native byte[] getRaw(int entry, byte[] defaultValue); + /** + * Sets default raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static boolean setDefaultRaw(int entry, long time, byte[] defaultValue) { return setDefaultRaw(entry, time, defaultValue, 0, defaultValue.length); } + /** + * Sets default raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @param start Value's offset into buffer. + * @param len Length of value in buffer. + * @return True if set succeeded. + */ public static native boolean setDefaultRaw(int entry, long time, byte[] defaultValue, int start, int len); + /** + * Sets default raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static boolean setDefaultRaw(int entry, long time, ByteBuffer defaultValue) { int pos = defaultValue.position(); return setDefaultRaw(entry, time, defaultValue, pos, defaultValue.limit() - pos); } + /** + * Sets default raw topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @param start Value's offset into buffer. + * @param len Length of value in buffer. + * @return True if set succeeded. + */ public static boolean setDefaultRaw(int entry, long time, ByteBuffer defaultValue, int start, int len) { if (defaultValue.isDirect()) { if (start < 0) { @@ -324,93 +957,361 @@ public static boolean setDefaultRaw(int entry, long time, ByteBuffer defaultValu } } + /** + * Sets default raw topic value buffer. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @param start Value's offset into buffer. + * @param len Length of value in buffer. + * @return True if set succeeded. + */ private static native boolean setDefaultRawBuffer(int entry, long time, ByteBuffer defaultValue, int start, int len); + /** + * Returns timestamped topic value as an atomic BooleanArray. + * + * @param subentry Subentry handle. + * @param defaultValue Default value. + * @return Timestamped topic value. + */ public static native TimestampedBooleanArray getAtomicBooleanArray( int subentry, boolean[] defaultValue); + /** + * Returns queued timestamped topic values. + * + * @param subentry Subentry handle. + * @return List of timestamped topic values. + */ public static native TimestampedBooleanArray[] readQueueBooleanArray(int subentry); + /** + * Returns queued topic values. + * + * @param subentry Subentry handle. + * @return List of topic values. + */ public static native boolean[][] readQueueValuesBooleanArray(int subentry); + /** + * Sets topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Topic value. + * @return True if set succeeded. + */ public static native boolean setBooleanArray(int entry, long time, boolean[] value); + /** + * Returns topic value. + * + * @param entry Entry handle. + * @param defaultValue Default value. + * @return Topic value. + */ public static native boolean[] getBooleanArray(int entry, boolean[] defaultValue); + /** + * Sets default topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static native boolean setDefaultBooleanArray(int entry, long time, boolean[] defaultValue); + /** + * Returns timestamped topic value as an atomic IntegerArray. + * + * @param subentry Subentry handle. + * @param defaultValue Default value. + * @return Timestamped topic value. + */ public static native TimestampedIntegerArray getAtomicIntegerArray( int subentry, long[] defaultValue); + /** + * Returns queued timestamped topic values. + * + * @param subentry Subentry handle. + * @return List of timestamped topic values. + */ public static native TimestampedIntegerArray[] readQueueIntegerArray(int subentry); + /** + * Returns queued topic values. + * + * @param subentry Subentry handle. + * @return List of topic values. + */ public static native long[][] readQueueValuesIntegerArray(int subentry); + /** + * Sets topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Topic value. + * @return True if set succeeded. + */ public static native boolean setIntegerArray(int entry, long time, long[] value); + /** + * Returns topic value. + * + * @param entry Entry handle. + * @param defaultValue Default value. + * @return Topic value. + */ public static native long[] getIntegerArray(int entry, long[] defaultValue); + /** + * Sets default topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static native boolean setDefaultIntegerArray(int entry, long time, long[] defaultValue); + /** + * Returns timestamped topic value as an atomic FloatArray. + * + * @param subentry Subentry handle. + * @param defaultValue Default value. + * @return Timestamped topic value. + */ public static native TimestampedFloatArray getAtomicFloatArray( int subentry, float[] defaultValue); + /** + * Returns queued timestamped topic values. + * + * @param subentry Subentry handle. + * @return List of timestamped topic values. + */ public static native TimestampedFloatArray[] readQueueFloatArray(int subentry); + /** + * Returns queued topic values. + * + * @param subentry Subentry handle. + * @return List of topic values. + */ public static native float[][] readQueueValuesFloatArray(int subentry); + /** + * Sets topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Topic value. + * @return True if set succeeded. + */ public static native boolean setFloatArray(int entry, long time, float[] value); + /** + * Returns topic value. + * + * @param entry Entry handle. + * @param defaultValue Default value. + * @return Topic value. + */ public static native float[] getFloatArray(int entry, float[] defaultValue); + /** + * Sets default topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static native boolean setDefaultFloatArray(int entry, long time, float[] defaultValue); + /** + * Returns timestamped topic value as an atomic DoubleArray. + * + * @param subentry Subentry handle. + * @param defaultValue Default value. + * @return Timestamped topic value. + */ public static native TimestampedDoubleArray getAtomicDoubleArray( int subentry, double[] defaultValue); + /** + * Returns queued timestamped topic values. + * + * @param subentry Subentry handle. + * @return List of timestamped topic values. + */ public static native TimestampedDoubleArray[] readQueueDoubleArray(int subentry); + /** + * Returns queued topic values. + * + * @param subentry Subentry handle. + * @return List of topic values. + */ public static native double[][] readQueueValuesDoubleArray(int subentry); + /** + * Sets topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Topic value. + * @return True if set succeeded. + */ public static native boolean setDoubleArray(int entry, long time, double[] value); + /** + * Returns topic value. + * + * @param entry Entry handle. + * @param defaultValue Default value. + * @return Topic value. + */ public static native double[] getDoubleArray(int entry, double[] defaultValue); + /** + * Sets default topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static native boolean setDefaultDoubleArray(int entry, long time, double[] defaultValue); + /** + * Returns timestamped topic value as an atomic StringArray. + * + * @param subentry Subentry handle. + * @param defaultValue Default value. + * @return Timestamped topic value. + */ public static native TimestampedStringArray getAtomicStringArray( int subentry, String[] defaultValue); + /** + * Returns queued timestamped topic values. + * + * @param subentry Subentry handle. + * @return List of timestamped topic values. + */ public static native TimestampedStringArray[] readQueueStringArray(int subentry); + /** + * Returns queued topic values. + * + * @param subentry Subentry handle. + * @return List of topic values. + */ public static native String[][] readQueueValuesStringArray(int subentry); + /** + * Sets topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param value Topic value. + * @return True if set succeeded. + */ public static native boolean setStringArray(int entry, long time, String[] value); + /** + * Returns topic value. + * + * @param entry Entry handle. + * @param defaultValue Default value. + * @return Topic value. + */ public static native String[] getStringArray(int entry, String[] defaultValue); + /** + * Sets default topic value. + * + * @param entry Entry handle. + * @param time Time in microseconds. + * @param defaultValue Default value. + * @return True if set succeeded. + */ public static native boolean setDefaultStringArray(int entry, long time, String[] defaultValue); + /** + * Returns queued subentry values. + * + * @param subentry Subentry handle. + * @return List of queued subentry values. + */ public static native NetworkTableValue[] readQueueValue(int subentry); + /** + * Returns entry's NT value. + * + * @param entry Entry handle. + * @return Entry's NT value. + */ public static native NetworkTableValue getValue(int entry); + /** + * Sets entry flags. + * + * @param entry Entry handle. + * @param flags Entry flags. + */ public static native void setEntryFlags(int entry, int flags); + /** + * Returns entry flags. + * + * @param entry Entry handle. + * @return Entry flags. + */ public static native int getEntryFlags(int entry); + /** + * Returns topic info. + * + * @param inst NT instance handle. + * @param topic Topic handle. + * @return Topic info. + */ public static native TopicInfo getTopicInfo(NetworkTableInstance inst, int topic); + /** + * Creates a listener poller. + * + * @param inst NT instance handle. + * @return Listener poller handle. + */ public static native int createListenerPoller(int inst); + /** + * Destroys listener poller. + * + * @param poller Listener poller handle. + */ public static native void destroyListenerPoller(int poller); + /** + * Converts NT event kinds to mask. + * + * @param kinds Enum set of NT event kinds. + * @return NT event mask. + */ private static int kindsToMask(EnumSet kinds) { int mask = 0; for (NetworkTableEvent.Kind kind : kinds) { @@ -419,79 +1320,318 @@ private static int kindsToMask(EnumSet kinds) { return mask; } + /** + * Adds listener. + * + * @param poller Listener poller handle. + * @param prefixes Topic prefixes. + * @param kinds Enum set of NT event kinds. + * @return Listener handle. + */ public static int addListener(int poller, String[] prefixes, EnumSet kinds) { return addListener(poller, prefixes, kindsToMask(kinds)); } + /** + * Adds listener. + * + * @param poller Listener poller handle. + * @param handle Topic handle. + * @param kinds Enum set of NT event kinds. + * @return Listener handle. + */ public static int addListener(int poller, int handle, EnumSet kinds) { return addListener(poller, handle, kindsToMask(kinds)); } + /** + * Adds listener. + * + * @param poller Listener poller handle. + * @param prefixes Topic prefixes. + * @param mask NT event mask. + * @return Listener handle. + */ public static native int addListener(int poller, String[] prefixes, int mask); + /** + * Adds listener. + * + * @param poller Listener poller handle. + * @param handle Topic handle. + * @param mask NT event mask. + * @return Listener handle. + */ public static native int addListener(int poller, int handle, int mask); + /** + * Returns NT events from listener queue. + * + * @param inst NT instance handle. + * @param poller Listener poller handle. + * @return List of NT events. + */ public static native NetworkTableEvent[] readListenerQueue( NetworkTableInstance inst, int poller); + /** + * Removes listener. + * + * @param listener Listener handle. + */ public static native void removeListener(int listener); + /** + * Returns network mode. + * + * @param inst NT instance handle. + * @return Network mode. + */ public static native int getNetworkMode(int inst); + /** + * Starts local-only operation. Prevents calls to startServer or startClient from taking effect. + * Has no effect if startServer or startClient has already been called. + * + * @param inst NT instance handle. + */ public static native void startLocal(int inst); + /** + * Stops local-only operation. startServer or startClient can be called after this call to start + * a server or client. + * + * @param inst NT instance handle. + */ public static native void stopLocal(int inst); + /** + * Starts a server using the specified filename, listening address, and port. + * + * @param inst NT instance handle. + * @param persistFilename the name of the persist file to use + * @param listenAddress the address to listen on, or empty to listen on any address + * @param port3 port to communicate over (NT3) + * @param port4 port to communicate over (NT4) + */ public static native void startServer( int inst, String persistFilename, String listenAddress, int port3, int port4); + /** + * Stops the server if it is running. + * + * @param inst NT instance handle. + */ public static native void stopServer(int inst); + /** + * Starts a NT3 client. Use SetServer or SetServerTeam to set the server name and port. + * + * @param inst NT instance handle. + * @param identity network identity to advertise (cannot be empty string) + */ public static native void startClient3(int inst, String identity); + /** + * Starts a NT4 client. Use SetServer or SetServerTeam to set the server name and port. + * + * @param inst NT instance handle. + * @param identity network identity to advertise (cannot be empty string) + */ public static native void startClient4(int inst, String identity); + /** + * Stops the client if it is running. + * + * @param inst NT instance handle. + */ public static native void stopClient(int inst); + /** + * Sets server address and port for client (without restarting client). + * + * @param inst NT instance handle. + * @param serverName server name + * @param port port to communicate over + */ public static native void setServer(int inst, String serverName, int port); + /** + * Sets server addresses and ports for client (without restarting client). The client will + * attempt to connect to each server in round robin fashion. + * + * @param inst NT instance handle. + * @param serverNames array of server names + * @param ports array of port numbers (0=default) + */ public static native void setServer(int inst, String[] serverNames, int[] ports); + /** + * Sets server addresses and port for client (without restarting client). Connects using commonly + * known robot addresses for the specified team. + * + * @param inst NT instance handle. + * @param team team number + * @param port port to communicate over + */ public static native void setServerTeam(int inst, int team, int port); + /** + * Disconnects the client if it's running and connected. This will automatically start + * reconnection attempts to the current server list. + * + * @param inst NT instance handle. + */ public static native void disconnect(int inst); + /** + * Starts requesting server address from Driver Station. This connects to the Driver Station + * running on localhost to obtain the server IP address. + * + * @param inst NT instance handle. + * @param port server port to use in combination with IP from DS + */ public static native void startDSClient(int inst, int port); + /** + * Stops requesting server address from Driver Station. + * + * @param inst NT instance handle. + */ public static native void stopDSClient(int inst); + /** + * Flushes all updated values immediately to the local client/server. This does not flush to the + * network. + * + * @param inst NT instance handle. + */ public static native void flushLocal(int inst); + /** + * Flushes all updated values immediately to the network. Note: This is rate-limited to protect + * the network from flooding. This is primarily useful for synchronizing network updates with + * user code. + * + * @param inst NT instance handle. + */ public static native void flush(int inst); + /** + * Gets information on the currently established network connections. If operating as a client, + * this will return either zero or one values. + * + * @param inst NT instance handle. + * @return array of connection information + */ public static native ConnectionInfo[] getConnections(int inst); + /** + * Return whether or not the instance is connected to another node. + * + * @param inst NT instance handle. + * @return True if connected. + */ public static native boolean isConnected(int inst); + /** + * Get the time offset between server time and local time. Add this value to local time to get + * the estimated equivalent server time. In server mode, this always returns 0. In client mode, + * this returns the time offset only if the client and server are connected and have exchanged + * synchronization messages. Note the time offset may change over time as it is periodically + * updated; to receive updates as events, add a listener to the "time sync" event. + * + * @param inst NT instance handle. + * @return Time offset in microseconds (optional) + */ public static native OptionalLong getServerTimeOffset(int inst); + /** + * Returns the current timestamp in microseconds. + * + * @return The current timestsamp in microseconds. + */ public static native long now(); + /** + * Starts logging entry changes to a DataLog. + * + * @param inst NT instance handle. + * @param log data log handle; lifetime must extend until StopEntryDataLog is called or the + * instance is destroyed + * @param prefix only store entries with names that start with this prefix; the prefix is not + * included in the data log entry name + * @param logPrefix prefix to add to data log entry names + * @return Data logger handle + */ private static native int startEntryDataLog(int inst, long log, String prefix, String logPrefix); + /** + * Starts logging entry changes to a DataLog. + * + * @param inst NT instance handle. + * @param log data log object; lifetime must extend until StopEntryDataLog is called or the + * instance is destroyed + * @param prefix only store entries with names that start with this prefix; the prefix is not + * included in the data log entry name + * @param logPrefix prefix to add to data log entry names + * @return Data logger handle + */ public static int startEntryDataLog(int inst, DataLog log, String prefix, String logPrefix) { return startEntryDataLog(inst, log.getImpl(), prefix, logPrefix); } + /** + * Stops logging entry changes to a DataLog. + * + * @param logger data logger handle + */ public static native void stopEntryDataLog(int logger); + /** + * Starts logging connection changes to a DataLog. + * + * @param inst NT instance handle. + * @param log data log handle; lifetime must extend until StopConnectionDataLog is called or the + * instance is destroyed + * @param name data log entry name + * @return Data logger handle + */ private static native int startConnectionDataLog(int inst, long log, String name); + /** + * Starts logging connection changes to a DataLog. + * + * @param inst NT instance handle. + * @param log data log object; lifetime must extend until StopConnectionDataLog is called or the + * instance is destroyed + * @param name data log entry name + * @return Data logger handle + */ public static int startConnectionDataLog(int inst, DataLog log, String name) { return startConnectionDataLog(inst, log.getImpl(), name); } + /** + * Stops logging connection changes to a DataLog. + * + * @param logger data logger handle + */ public static native void stopConnectionDataLog(int logger); + /** + * Add logger callback function. By default, log messages are sent to stderr; this function sends + * log messages with the specified levels to the provided callback function instead. The callback + * function will only be called for log messages with level greater than or equal to minLevel and + * less than or equal to maxLevel; messages outside this range will be silently ignored. + * + * @param poller Listener poller handle. + * @param minLevel minimum log level + * @param maxLevel maximum log level + * @return Listener handle + */ public static native int addLogger(int poller, int minLevel, int maxLevel); + + /** Utility class. */ + private NetworkTablesJNI() {} } diff --git a/ntcore/src/main/java/edu/wpi/first/networktables/EntryBase.java b/ntcore/src/main/java/edu/wpi/first/networktables/EntryBase.java index 48d4a9c3627..2083423d930 100644 --- a/ntcore/src/main/java/edu/wpi/first/networktables/EntryBase.java +++ b/ntcore/src/main/java/edu/wpi/first/networktables/EntryBase.java @@ -40,5 +40,6 @@ public long getLastChange() { return NetworkTablesJNI.getEntryLastChange(m_handle); } + /** NetworkTables handle. */ protected int m_handle; } diff --git a/ntcore/src/main/java/edu/wpi/first/networktables/LogMessage.java b/ntcore/src/main/java/edu/wpi/first/networktables/LogMessage.java index fd090afff0c..3f268405b63 100644 --- a/ntcore/src/main/java/edu/wpi/first/networktables/LogMessage.java +++ b/ntcore/src/main/java/edu/wpi/first/networktables/LogMessage.java @@ -7,16 +7,31 @@ /** NetworkTables log message. */ @SuppressWarnings("MemberName") public final class LogMessage { - /** Logging levels. */ + /** Critical logging level. */ public static final int kCritical = 50; + /** Error logging level. */ public static final int kError = 40; + + /** Warning log level. */ public static final int kWarning = 30; + + /** Info log level. */ public static final int kInfo = 20; + + /** Debug log level. */ public static final int kDebug = 10; + + /** Debug log level 1. */ public static final int kDebug1 = 9; + + /** Debug log level 2. */ public static final int kDebug2 = 8; + + /** Debug log level 3. */ public static final int kDebug3 = 7; + + /** Debug log level 4. */ public static final int kDebug4 = 6; /** Log level of the message. */ diff --git a/ntcore/src/main/java/edu/wpi/first/networktables/NTSendableBuilder.java b/ntcore/src/main/java/edu/wpi/first/networktables/NTSendableBuilder.java index 4d5e6a5330d..ce09d693de1 100644 --- a/ntcore/src/main/java/edu/wpi/first/networktables/NTSendableBuilder.java +++ b/ntcore/src/main/java/edu/wpi/first/networktables/NTSendableBuilder.java @@ -6,6 +6,7 @@ import edu.wpi.first.util.sendable.SendableBuilder; +/** Helper class for building Sendable dashboard representations for NetworkTables. */ public interface NTSendableBuilder extends SendableBuilder { /** * Set the function that should be called to update the network table for things other than diff --git a/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTable.java b/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTable.java index c88ce8b3c87..a9768ff7e89 100644 --- a/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTable.java +++ b/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTable.java @@ -330,7 +330,7 @@ public NetworkTable getSubTable(String key) { * @return true if the table as a value assigned to the given key */ public boolean containsKey(String key) { - return !("".equals(key)) && getTopic(key).exists(); + return !"".equals(key) && getTopic(key).exists(); } /** diff --git a/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTableEvent.java b/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTableEvent.java index 97883c8c390..55f040c96a8 100644 --- a/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTableEvent.java +++ b/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTableEvent.java @@ -12,6 +12,7 @@ */ @SuppressWarnings("MemberName") public final class NetworkTableEvent { + /** NetworkTable event kind. */ public enum Kind { /** * Initial listener addition. Set this to receive immediate notification of matches to other @@ -61,6 +62,11 @@ public enum Kind { this.value = value; } + /** + * Returns the NetworkTable event kind value. + * + * @return The NetworkTable event kind value. + */ public int getValue() { return value; } diff --git a/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTableType.java b/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTableType.java index 2350b49bba6..89a5413b1cd 100644 --- a/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTableType.java +++ b/ntcore/src/main/java/edu/wpi/first/networktables/NetworkTableType.java @@ -6,17 +6,29 @@ /** Network table data types. */ public enum NetworkTableType { + /** Unassigned data type. */ kUnassigned(0, ""), + /** Boolean data type. */ kBoolean(0x01, "boolean"), + /** Double precision floating-point data type. */ kDouble(0x02, "double"), + /** String data type. */ kString(0x04, "string"), + /** Raw data type. */ kRaw(0x08, "raw"), + /** Boolean array data type. */ kBooleanArray(0x10, "boolean[]"), + /** Double precision floating-point array data type. */ kDoubleArray(0x20, "double[]"), + /** String array data type. */ kStringArray(0x40, "string[]"), + /** Integer data type. */ kInteger(0x100, "int"), + /** Single precision floating-point data type. */ kFloat(0x200, "float"), + /** Integer array data type. */ kIntegerArray(0x400, "int[]"), + /** Single precision floating-point array data type. */ kFloatArray(0x800, "float[]"); private final int m_value; @@ -27,10 +39,20 @@ public enum NetworkTableType { m_valueStr = valueStr; } + /** + * Returns the NetworkTable type value. + * + * @return The NetworkTable type value. + */ public int getValue() { return m_value; } + /** + * Returns the NetworkTable type value as as string. + * + * @return The NetworkTable type value as a string. + */ public String getValueStr() { return m_valueStr; } diff --git a/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufTopic.java b/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufTopic.java index 15313d7f683..eb9a489d75c 100644 --- a/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufTopic.java +++ b/ntcore/src/main/java/edu/wpi/first/networktables/ProtobufTopic.java @@ -153,6 +153,11 @@ public ProtobufEntry getEntry(T defaultValue, PubSubOption... options) { false); } + /** + * Returns the protobuf. + * + * @return The protobuf. + */ public Protobuf getProto() { return m_proto; } diff --git a/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayTopic.java b/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayTopic.java index 557e9ddf4ef..6f22179258a 100644 --- a/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayTopic.java +++ b/ntcore/src/main/java/edu/wpi/first/networktables/StructArrayTopic.java @@ -153,6 +153,11 @@ public StructArrayEntry getEntry(T[] defaultValue, PubSubOption... options) { false); } + /** + * Returns the struct. + * + * @return The struct. + */ public Struct getStruct() { return m_struct; } diff --git a/ntcore/src/main/java/edu/wpi/first/networktables/StructTopic.java b/ntcore/src/main/java/edu/wpi/first/networktables/StructTopic.java index 22dbd2d7a17..1212ead7003 100644 --- a/ntcore/src/main/java/edu/wpi/first/networktables/StructTopic.java +++ b/ntcore/src/main/java/edu/wpi/first/networktables/StructTopic.java @@ -152,6 +152,11 @@ public StructEntry getEntry(T defaultValue, PubSubOption... options) { false); } + /** + * Returns the struct. + * + * @return The struct. + */ public Struct getStruct() { return m_struct; } diff --git a/ntcore/src/main/java/edu/wpi/first/networktables/TimestampedObject.java b/ntcore/src/main/java/edu/wpi/first/networktables/TimestampedObject.java index 37c15447936..896ec795a3b 100644 --- a/ntcore/src/main/java/edu/wpi/first/networktables/TimestampedObject.java +++ b/ntcore/src/main/java/edu/wpi/first/networktables/TimestampedObject.java @@ -4,7 +4,11 @@ package edu.wpi.first.networktables; -/** NetworkTables timestamped object. */ +/** + * NetworkTables timestamped object. + * + * @param Value type. + */ public final class TimestampedObject { /** * Create a timestamped value. diff --git a/ntcore/src/main/java/edu/wpi/first/networktables/Topic.java b/ntcore/src/main/java/edu/wpi/first/networktables/Topic.java index 6794474566b..358814c4c21 100644 --- a/ntcore/src/main/java/edu/wpi/first/networktables/Topic.java +++ b/ntcore/src/main/java/edu/wpi/first/networktables/Topic.java @@ -336,6 +336,9 @@ public int hashCode() { return m_handle; } + /** NetworkTables instance. */ protected NetworkTableInstance m_inst; + + /** NetworkTables handle. */ protected int m_handle; } diff --git a/ntcore/src/main/native/cpp/LocalStorage.cpp b/ntcore/src/main/native/cpp/LocalStorage.cpp index 8b0cb02e3ef..069b2e49e1e 100644 --- a/ntcore/src/main/native/cpp/LocalStorage.cpp +++ b/ntcore/src/main/native/cpp/LocalStorage.cpp @@ -46,10 +46,16 @@ bool LocalStorage::MultiSubscriberData::Matches(std::string_view name, } int LocalStorage::DataLoggerData::Start(TopicData* topic, int64_t time) { + std::string_view typeStr = topic->typeStr; + // NT and DataLog use different standard representations for int and int[] + if (typeStr == "int") { + typeStr = "int64"; + } else if (typeStr == "int[]") { + typeStr = "int64[]"; + } return log.Start( fmt::format("{}{}", logPrefix, wpi::remove_prefix(topic->name, prefix)), - topic->typeStr == "int" ? "int64" : topic->typeStr, - DataLoggerEntry::MakeMetadata(topic->propertiesStr), time); + typeStr, DataLoggerEntry::MakeMetadata(topic->propertiesStr), time); } void LocalStorage::DataLoggerEntry::Append(const Value& v) { diff --git a/ntcore/src/main/native/cpp/LocalStorage.h b/ntcore/src/main/native/cpp/LocalStorage.h index 4283274d86c..44951137e61 100644 --- a/ntcore/src/main/native/cpp/LocalStorage.h +++ b/ntcore/src/main/native/cpp/LocalStorage.h @@ -254,13 +254,13 @@ class LocalStorage final : public net::ILocalStorage { wpi::SmallVectorImpl::SmallElem>& buf, typename TypeInfo::View defaultValue); - std::vector ReadQueueValue(NT_Handle subentry) { + std::vector ReadQueueValue(NT_Handle subentry, unsigned int types) { std::scoped_lock lock{m_mutex}; auto subscriber = m_impl.GetSubEntry(subentry); if (!subscriber) { return {}; } - return subscriber->pollStorage.ReadValue(); + return subscriber->pollStorage.ReadValue(types); } template diff --git a/ntcore/src/main/native/cpp/NetworkClient.cpp b/ntcore/src/main/native/cpp/NetworkClient.cpp index 149dcb690b7..477ade1a72d 100644 --- a/ntcore/src/main/native/cpp/NetworkClient.cpp +++ b/ntcore/src/main/native/cpp/NetworkClient.cpp @@ -362,6 +362,7 @@ void NetworkClient::HandleLocal() { } void NetworkClient::TcpConnected(uv::Tcp& tcp) { + tcp.SetLogger(&m_logger); tcp.SetNoDelay(true); // Start the WS client if (m_logger.min_level() >= wpi::WPI_LOG_DEBUG4) { @@ -401,9 +402,8 @@ void NetworkClient::WsConnected(wpi::WebSocket& ws, uv::Tcp& tcp, INFO("CONNECTED NT4 to {} port {}", connInfo.remote_ip, connInfo.remote_port); m_connHandle = m_connList.AddConnection(connInfo); - m_wire = - std::make_shared(ws, connInfo.protocol_version); - m_wire->Start(); + m_wire = std::make_shared( + ws, connInfo.protocol_version, m_logger); m_clientImpl = std::make_unique( m_loop.Now().count(), m_inst, *m_wire, m_logger, m_timeSyncUpdated, [this](uint32_t repeatMs) { diff --git a/ntcore/src/main/native/cpp/NetworkServer.cpp b/ntcore/src/main/native/cpp/NetworkServer.cpp index 66925f73772..8d1869e7fb4 100644 --- a/ntcore/src/main/native/cpp/NetworkServer.cpp +++ b/ntcore/src/main/native/cpp/NetworkServer.cpp @@ -242,7 +242,7 @@ void NetworkServer::ServerConnection4::ProcessWsUpgrade() { m_info.protocol_version = protocol == "v4.1.networktables.first.wpi.edu" ? 0x0401 : 0x0400; m_wire = std::make_shared( - *m_websocket, m_info.protocol_version); + *m_websocket, m_info.protocol_version, m_logger); if (protocol == "rtt.networktables.first.wpi.edu") { INFO("CONNECTED RTT client (from {})", m_connInfo); @@ -281,7 +281,6 @@ void NetworkServer::ServerConnection4::ProcessWsUpgrade() { INFO("CONNECTED NT4 client '{}' (from {})", dedupName, m_connInfo); m_info.remote_id = dedupName; m_server.AddConnection(this, m_info); - m_wire->Start(); m_websocket->closed.connect([this](uint16_t, std::string_view reason) { auto realReason = m_wire->GetDisconnectReason(); INFO("DISCONNECTED NT4 client '{}' (from {}): {}", m_info.remote_id, @@ -500,6 +499,7 @@ void NetworkServer::Init() { if (!tcp) { return; } + tcp->SetLogger(&m_logger); tcp->error.connect([logger = &m_logger](uv::Error err) { WPI_INFO(*logger, "NT4 socket error: {}", err.str()); }); diff --git a/ntcore/src/main/native/cpp/ValueCircularBuffer.cpp b/ntcore/src/main/native/cpp/ValueCircularBuffer.cpp index f611a335d98..55c2968616e 100644 --- a/ntcore/src/main/native/cpp/ValueCircularBuffer.cpp +++ b/ntcore/src/main/native/cpp/ValueCircularBuffer.cpp @@ -6,10 +6,13 @@ using namespace nt; -std::vector ValueCircularBuffer::ReadValue() { +std::vector ValueCircularBuffer::ReadValue(unsigned int types) { std::vector rv; rv.reserve(m_storage.size()); for (auto&& val : m_storage) { + if (types != 0 && (types & val.type()) == 0) { + continue; + } rv.emplace_back(std::move(val)); } m_storage.reset(); diff --git a/ntcore/src/main/native/cpp/ValueCircularBuffer.h b/ntcore/src/main/native/cpp/ValueCircularBuffer.h index b80b5db6bbb..9ea01718a77 100644 --- a/ntcore/src/main/native/cpp/ValueCircularBuffer.h +++ b/ntcore/src/main/native/cpp/ValueCircularBuffer.h @@ -24,7 +24,7 @@ class ValueCircularBuffer { m_storage.emplace_back(std::forward(args...)); } - std::vector ReadValue(); + std::vector ReadValue(unsigned int types); template std::vector::Value>> Read(); diff --git a/ntcore/src/main/native/cpp/net/ClientImpl.cpp b/ntcore/src/main/native/cpp/net/ClientImpl.cpp index 4f7e9cb7a96..96a6be76410 100644 --- a/ntcore/src/main/native/cpp/net/ClientImpl.cpp +++ b/ntcore/src/main/native/cpp/net/ClientImpl.cpp @@ -67,26 +67,28 @@ void ClientImpl::ProcessIncomingBinary(uint64_t curTimeMs, DEBUG4("BinaryMessage({})", id); // handle RTT ping response (only use first one) - if (!m_haveTimeOffset && id == -1) { - if (!value.IsInteger()) { - WARN("RTT ping response with non-integer type {}", - static_cast(value.type())); - continue; - } - DEBUG4("RTT ping response time {} value {}", value.time(), - value.GetInteger()); - if (m_wire.GetVersion() < 0x0401) { - m_pongTimeMs = curTimeMs; - } - int64_t now = wpi::Now(); - int64_t rtt2 = (now - value.GetInteger()) / 2; - if (rtt2 < m_rtt2Us) { - m_rtt2Us = rtt2; - int64_t serverTimeOffsetUs = value.server_time() + rtt2 - now; - DEBUG3("Time offset: {}", serverTimeOffsetUs); - m_outgoing.SetTimeOffset(serverTimeOffsetUs); - m_haveTimeOffset = true; - m_timeSyncUpdated(serverTimeOffsetUs, m_rtt2Us, true); + if (id == -1) { + if (!m_haveTimeOffset) { + if (!value.IsInteger()) { + WARN("RTT ping response with non-integer type {}", + static_cast(value.type())); + continue; + } + DEBUG4("RTT ping response time {} value {}", value.time(), + value.GetInteger()); + if (m_wire.GetVersion() < 0x0401) { + m_pongTimeMs = curTimeMs; + } + int64_t now = wpi::Now(); + int64_t rtt2 = (now - value.GetInteger()) / 2; + if (rtt2 < m_rtt2Us) { + m_rtt2Us = rtt2; + int64_t serverTimeOffsetUs = value.server_time() + rtt2 - now; + DEBUG3("Time offset: {}", serverTimeOffsetUs); + m_outgoing.SetTimeOffset(serverTimeOffsetUs); + m_haveTimeOffset = true; + m_timeSyncUpdated(serverTimeOffsetUs, m_rtt2Us, true); + } } continue; } diff --git a/ntcore/src/main/native/cpp/net/NetworkPing.cpp b/ntcore/src/main/native/cpp/net/NetworkPing.cpp index fdbd26c132a..e3d55a2b31b 100644 --- a/ntcore/src/main/native/cpp/net/NetworkPing.cpp +++ b/ntcore/src/main/native/cpp/net/NetworkPing.cpp @@ -12,14 +12,15 @@ bool NetworkPing::Send(uint64_t curTimeMs) { if (curTimeMs < m_nextPingTimeMs) { return true; } - // if we didn't receive a timely response to our last ping, disconnect - uint64_t lastPing = m_wire.GetLastPingResponse(); + // if we haven't received data in a while, disconnect + // (we should at least be getting PONG responses) + uint64_t lastData = m_wire.GetLastReceivedTime(); // DEBUG4("WS ping: lastPing={} curTime={} pongTimeMs={}\n", lastPing, // curTimeMs, m_pongTimeMs); - if (lastPing == 0) { - lastPing = m_pongTimeMs; + if (lastData == 0) { + lastData = m_pongTimeMs; } - if (m_pongTimeMs != 0 && curTimeMs > (lastPing + kPingTimeoutMs)) { + if (m_pongTimeMs != 0 && curTimeMs > (lastData + kPingTimeoutMs)) { m_wire.Disconnect("connection timed out"); return false; } diff --git a/ntcore/src/main/native/cpp/net/WebSocketConnection.cpp b/ntcore/src/main/native/cpp/net/WebSocketConnection.cpp index dcfbabe240d..176770db7f6 100644 --- a/ntcore/src/main/native/cpp/net/WebSocketConnection.cpp +++ b/ntcore/src/main/native/cpp/net/WebSocketConnection.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -52,10 +53,20 @@ class WebSocketConnection::Stream final : public wpi::raw_ostream { void WebSocketConnection::Stream::write_impl(const char* data, size_t len) { if (data == m_conn.m_bufs.back().base) { // flush_nonempty() case + size_t amt = len - m_conn.m_bufs.back().len; + WPI_DEBUG4(m_conn.m_logger, "conn: writing {} bytes (nonempty)", amt); m_conn.m_bufs.back().len = len; + m_conn.m_framePos += amt; + m_conn.m_written += amt; if (!m_disableAlloc) { +#ifdef NT_ENABLE_WS_FRAG m_conn.m_frames.back().opcode &= ~wpi::WebSocket::kFlagFin; m_conn.StartFrame(wpi::WebSocket::Frame::kFragment); +#else + m_conn.m_bufs.emplace_back(m_conn.AllocBuf()); + m_conn.m_bufs.back().len = 0; + ++m_conn.m_frames.back().end; +#endif SetBuffer(m_conn.m_bufs.back().base, kAllocSize); } return; @@ -68,6 +79,7 @@ void WebSocketConnection::Stream::write_impl(const char* data, size_t len) { size_t amt = (std::min)(static_cast(kAllocSize - buf.len), static_cast(len)); if (amt > 0) { + WPI_DEBUG4(m_conn.m_logger, "conn: writing {} bytes", amt); std::memcpy(buf.base + buf.len, data, amt); buf.len += amt; m_conn.m_framePos += amt; @@ -76,9 +88,15 @@ void WebSocketConnection::Stream::write_impl(const char* data, size_t len) { len -= amt; } if (buf.len >= kAllocSize && (len > 0 || !m_disableAlloc)) { +#ifdef NT_ENABLE_WS_FRAG // fragment the current frame and start a new one m_conn.m_frames.back().opcode &= ~wpi::WebSocket::kFlagFin; m_conn.StartFrame(wpi::WebSocket::Frame::kFragment); +#else + m_conn.m_bufs.emplace_back(m_conn.AllocBuf()); + m_conn.m_bufs.back().len = 0; + ++m_conn.m_frames.back().end; +#endif updateBuffer = true; } } @@ -89,8 +107,9 @@ void WebSocketConnection::Stream::write_impl(const char* data, size_t len) { } WebSocketConnection::WebSocketConnection(wpi::WebSocket& ws, - unsigned int version) - : m_ws{ws}, m_version{version} {} + unsigned int version, + wpi::Logger& logger) + : m_ws{ws}, m_logger{logger}, m_version{version} {} WebSocketConnection::~WebSocketConnection() { for (auto&& buf : m_bufs) { @@ -101,19 +120,8 @@ WebSocketConnection::~WebSocketConnection() { } } -void WebSocketConnection::Start() { - m_ws.pong.connect([selfweak = weak_from_this()](auto data) { - if (data.size() != 8) { - return; - } - if (auto self = selfweak.lock()) { - self->m_lastPingResponse = - wpi::support::endian::read64(data.data()); - } - }); -} - void WebSocketConnection::SendPing(uint64_t time) { + WPI_DEBUG4(m_logger, "conn: sending ping {}", time); auto buf = AllocBuf(); buf.len = 8; wpi::support::endian::write64(buf.base, time); @@ -130,6 +138,8 @@ void WebSocketConnection::SendPing(uint64_t time) { } void WebSocketConnection::StartFrame(uint8_t opcode) { + WPI_DEBUG4(m_logger, "conn: starting frame {}", + static_cast(opcode)); m_frames.emplace_back(opcode, m_bufs.size(), m_bufs.size() + 1); m_bufs.emplace_back(AllocBuf()); m_bufs.back().len = 0; @@ -165,6 +175,7 @@ int WebSocketConnection::Write( if (kind == kText) { os << (first ? '[' : ','); } + WPI_DEBUG4(m_logger, "writing"); writer(os); } ++m_frames.back().count; @@ -176,6 +187,7 @@ int WebSocketConnection::Write( } int WebSocketConnection::Flush() { + WPI_DEBUG4(m_logger, "conn: flushing"); m_lastFlushTime = wpi::Now(); if (m_state == kEmpty) { return 0; @@ -240,6 +252,7 @@ void WebSocketConnection::Send( os << ']'; } wpi::WebSocket::Frame frame{opcode, os.bufs()}; + WPI_DEBUG4(m_logger, "Send({})", static_cast(opcode)); m_ws.SendFrames({{frame}}, [selfweak = weak_from_this()](auto bufs, auto) { if (auto self = selfweak.lock()) { self->ReleaseBufs(bufs); @@ -253,7 +266,7 @@ void WebSocketConnection::Send( void WebSocketConnection::Disconnect(std::string_view reason) { m_reason = reason; - m_ws.Fail(1005, reason); + m_ws.Fail(1001, reason); } wpi::uv::Buffer WebSocketConnection::AllocBuf() { diff --git a/ntcore/src/main/native/cpp/net/WebSocketConnection.h b/ntcore/src/main/native/cpp/net/WebSocketConnection.h index 4398451532a..fc5cfe98330 100644 --- a/ntcore/src/main/native/cpp/net/WebSocketConnection.h +++ b/ntcore/src/main/native/cpp/net/WebSocketConnection.h @@ -15,19 +15,22 @@ #include "WireConnection.h" +namespace wpi { +class Logger; +} // namespace wpi + namespace nt::net { class WebSocketConnection final : public WireConnection, public std::enable_shared_from_this { public: - WebSocketConnection(wpi::WebSocket& ws, unsigned int version); + WebSocketConnection(wpi::WebSocket& ws, unsigned int version, + wpi::Logger& logger); ~WebSocketConnection() override; WebSocketConnection(const WebSocketConnection&) = delete; WebSocketConnection& operator=(const WebSocketConnection&) = delete; - void Start(); - unsigned int GetVersion() const final { return m_version; } void SendPing(uint64_t time) final; @@ -51,7 +54,9 @@ class WebSocketConnection final uint64_t GetLastFlushTime() const final { return m_lastFlushTime; } - uint64_t GetLastPingResponse() const final { return m_lastPingResponse; } + uint64_t GetLastReceivedTime() const final { + return m_ws.GetLastReceivedTime(); + } void Disconnect(std::string_view reason) final; @@ -70,6 +75,7 @@ class WebSocketConnection final void ReleaseBufs(std::span bufs); wpi::WebSocket& m_ws; + wpi::Logger& m_logger; class Stream; @@ -92,7 +98,6 @@ class WebSocketConnection final State m_state = kEmpty; std::string m_reason; uint64_t m_lastFlushTime = 0; - uint64_t m_lastPingResponse = 0; unsigned int m_version; }; diff --git a/ntcore/src/main/native/cpp/net/WireConnection.h b/ntcore/src/main/native/cpp/net/WireConnection.h index 2c876cc29f3..c1efaff2f8c 100644 --- a/ntcore/src/main/native/cpp/net/WireConnection.h +++ b/ntcore/src/main/native/cpp/net/WireConnection.h @@ -51,8 +51,8 @@ class WireConnection { virtual uint64_t GetLastFlushTime() const = 0; // in microseconds - // Gets the timestamp of the last ping we got a reply to - virtual uint64_t GetLastPingResponse() const = 0; // in microseconds + // Gets the timestamp of the last incoming data + virtual uint64_t GetLastReceivedTime() const = 0; // in microseconds virtual void Disconnect(std::string_view reason) = 0; }; diff --git a/ntcore/src/main/native/cpp/ntcore_c.cpp b/ntcore/src/main/native/cpp/ntcore_c.cpp index c4322b31a93..c2561c8833d 100644 --- a/ntcore/src/main/native/cpp/ntcore_c.cpp +++ b/ntcore/src/main/native/cpp/ntcore_c.cpp @@ -175,11 +175,19 @@ uint64_t NT_GetEntryLastChange(NT_Entry entry) { } void NT_GetEntryValue(NT_Entry entry, struct NT_Value* value) { + NT_GetEntryValueType(entry, 0, value); +} + +void NT_GetEntryValueType(NT_Entry entry, unsigned int types, + struct NT_Value* value) { NT_InitValue(value); auto v = nt::GetEntryValue(entry); if (!v) { return; } + if (types != 0 && (types & v.type()) == 0) { + return; + } ConvertToC(v, value); } @@ -204,6 +212,11 @@ struct NT_Value* NT_ReadQueueValue(NT_Handle subentry, size_t* count) { return ConvertToC(nt::ReadQueueValue(subentry), count); } +struct NT_Value* NT_ReadQueueValueType(NT_Handle subentry, unsigned int types, + size_t* count) { + return ConvertToC(nt::ReadQueueValue(subentry, types), count); +} + NT_Topic* NT_GetTopics(NT_Inst inst, const char* prefix, size_t prefix_len, unsigned int types, size_t* count) { auto info_v = nt::GetTopics(inst, {prefix, prefix_len}, types); diff --git a/ntcore/src/main/native/cpp/ntcore_cpp.cpp b/ntcore/src/main/native/cpp/ntcore_cpp.cpp index b27c4f5357d..33920ea48fe 100644 --- a/ntcore/src/main/native/cpp/ntcore_cpp.cpp +++ b/ntcore/src/main/native/cpp/ntcore_cpp.cpp @@ -144,8 +144,12 @@ unsigned int GetEntryFlags(NT_Entry entry) { } std::vector ReadQueueValue(NT_Handle subentry) { + return ReadQueueValue(subentry, 0); +} + +std::vector ReadQueueValue(NT_Handle subentry, unsigned int types) { if (auto ii = InstanceImpl::GetHandle(subentry)) { - return ii->localStorage.ReadQueueValue(subentry); + return ii->localStorage.ReadQueueValue(subentry, types); } else { return {}; } diff --git a/ntcore/src/main/native/include/networktables/NTSendableBuilder.h b/ntcore/src/main/native/include/networktables/NTSendableBuilder.h index 01ce6cc6aa6..3c6fe0e3f05 100644 --- a/ntcore/src/main/native/include/networktables/NTSendableBuilder.h +++ b/ntcore/src/main/native/include/networktables/NTSendableBuilder.h @@ -15,6 +15,10 @@ namespace nt { +/** + * Helper class for building Sendable dashboard representations for + * NetworkTables. + */ class NTSendableBuilder : public wpi::SendableBuilder { public: /** diff --git a/ntcore/src/main/native/include/networktables/NetworkTable.h b/ntcore/src/main/native/include/networktables/NetworkTable.h index e2d14212baa..10088591715 100644 --- a/ntcore/src/main/native/include/networktables/NetworkTable.h +++ b/ntcore/src/main/native/include/networktables/NetworkTable.h @@ -246,22 +246,27 @@ class NetworkTable final { * Gets a raw struct serialized value topic. * * @param name topic name + * @param info optional struct type info * @return Topic */ - template - StructTopic GetStructTopic(std::string_view name) const { - return StructTopic{GetTopic(name)}; + template + requires wpi::StructSerializable + StructTopic GetStructTopic(std::string_view name, I... info) const { + return StructTopic{GetTopic(name), std::move(info)...}; } /** * Gets a raw struct serialized array topic. * * @param name topic name + * @param info optional struct type info * @return Topic */ - template - StructArrayTopic GetStructArrayTopic(std::string_view name) const { - return StructArrayTopic{GetTopic(name)}; + template + requires wpi::StructSerializable + StructArrayTopic GetStructArrayTopic(std::string_view name, + I... info) const { + return StructArrayTopic{GetTopic(name), std::move(info)...}; } /** diff --git a/ntcore/src/main/native/include/networktables/NetworkTableType.h b/ntcore/src/main/native/include/networktables/NetworkTableType.h index 4b604543654..3b3e0861552 100644 --- a/ntcore/src/main/native/include/networktables/NetworkTableType.h +++ b/ntcore/src/main/native/include/networktables/NetworkTableType.h @@ -13,17 +13,29 @@ namespace nt { * @ingroup ntcore_cpp_api */ enum class NetworkTableType { + /// Unassigned data type. kUnassigned = NT_UNASSIGNED, + /// Boolean data type. kBoolean = NT_BOOLEAN, + /// Double precision floating-point data type. kDouble = NT_DOUBLE, + /// String data type. kString = NT_STRING, + /// Raw data type. kRaw = NT_RAW, + /// Boolean array data type. kBooleanArray = NT_BOOLEAN_ARRAY, + /// Double precision floating-point array data type. kDoubleArray = NT_DOUBLE_ARRAY, + /// String array data type. kStringArray = NT_STRING_ARRAY, + /// Integer data type. kInteger = NT_INTEGER, + /// Single precision floating-point data type. kFloat = NT_FLOAT, + /// Integer array data type. kIntegerArray = NT_INTEGER_ARRAY, + /// Single precision floating-point array data type. kFloatArray = NT_FLOAT_ARRAY }; diff --git a/ntcore/src/main/native/include/networktables/StructArrayTopic.h b/ntcore/src/main/native/include/networktables/StructArrayTopic.h index 667570bd7e9..c4f00836882 100644 --- a/ntcore/src/main/native/include/networktables/StructArrayTopic.h +++ b/ntcore/src/main/native/include/networktables/StructArrayTopic.h @@ -312,6 +312,9 @@ class StructArrayPublisher : public Publisher { void Set(std::span value, int64_t time = 0) { std::apply( [&](const I&... info) { + if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) { + GetTopic().GetInstance().template AddStructSchema(info...); + } m_buf.Write( value, [&](auto bytes) { ::nt::SetRaw(m_pubHandle, bytes, time); }, @@ -356,6 +359,9 @@ class StructArrayPublisher : public Publisher { void SetDefault(std::span value) { std::apply( [&](const I&... info) { + if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) { + GetTopic().GetInstance().template AddStructSchema(info...); + } m_buf.Write( value, [&](auto bytes) { ::nt::SetDefaultRaw(m_pubHandle, bytes); }, diff --git a/ntcore/src/main/native/include/networktables/Topic.h b/ntcore/src/main/native/include/networktables/Topic.h index eedd4871598..e34d7facc1c 100644 --- a/ntcore/src/main/native/include/networktables/Topic.h +++ b/ntcore/src/main/native/include/networktables/Topic.h @@ -396,6 +396,7 @@ class Publisher { Publisher() = default; explicit Publisher(NT_Publisher handle) : m_pubHandle{handle} {} + /// NetworkTables handle. NT_Publisher m_pubHandle{0}; }; diff --git a/ntcore/src/main/native/include/ntcore_c.h b/ntcore/src/main/native/include/ntcore_c.h index a335f813dd1..6259a76e9d9 100644 --- a/ntcore/src/main/native/include/ntcore_c.h +++ b/ntcore/src/main/native/include/ntcore_c.h @@ -464,6 +464,24 @@ uint64_t NT_GetEntryLastChange(NT_Entry entry); */ void NT_GetEntryValue(NT_Entry entry, struct NT_Value* value); +/** + * Get Entry Value. + * + * Returns copy of current entry value. + * Note that one of the type options is "unassigned". + * + * @param entry entry handle + * @param types bitmask of NT_Type values; 0 is treated specially + * as a "don't care" + * @param value storage for returned entry value + * + * It is the caller's responsibility to free value once it's no longer + * needed (the utility function NT_DisposeValue() is useful for this + * purpose). + */ +void NT_GetEntryValueType(NT_Entry entry, unsigned int types, + struct NT_Value* value); + /** * Set Default Entry Value. * @@ -518,6 +536,21 @@ unsigned int NT_GetEntryFlags(NT_Entry entry); */ struct NT_Value* NT_ReadQueueValue(NT_Handle subentry, size_t* count); +/** + * Read Entry Queue. + * + * Returns new entry values since last call. The returned array must be freed + * using NT_DisposeValueArray(). + * + * @param subentry subscriber or entry handle + * @param types bitmask of NT_Type values; 0 is treated specially + * as a "don't care" + * @param count count of items in returned array (output) + * @return entry value array; returns NULL and count=0 if no new values + */ +struct NT_Value* NT_ReadQueueValueType(NT_Handle subentry, unsigned int types, + size_t* count); + /** @} */ /** diff --git a/ntcore/src/main/native/include/ntcore_cpp.h b/ntcore/src/main/native/include/ntcore_cpp.h index e2529e1d335..fe50f454376 100644 --- a/ntcore/src/main/native/include/ntcore_cpp.h +++ b/ntcore/src/main/native/include/ntcore_cpp.h @@ -528,6 +528,18 @@ unsigned int GetEntryFlags(NT_Entry entry); */ std::vector ReadQueueValue(NT_Handle subentry); +/** + * Read Entry Queue. + * + * Returns new entry values since last call. + * + * @param subentry subscriber or entry handle + * @param types bitmask of NT_Type values; 0 is treated specially + * as a "don't care" + * @return entry value array + */ +std::vector ReadQueueValue(NT_Handle subentry, unsigned int types); + /** @} */ /** diff --git a/ntcore/src/test/native/cpp/net/MockWireConnection.h b/ntcore/src/test/native/cpp/net/MockWireConnection.h index 797ca253b7d..cc2d19b1715 100644 --- a/ntcore/src/test/native/cpp/net/MockWireConnection.h +++ b/ntcore/src/test/native/cpp/net/MockWireConnection.h @@ -63,7 +63,7 @@ class MockWireConnection : public WireConnection { MOCK_METHOD(int, Flush, (), (override)); MOCK_METHOD(uint64_t, GetLastFlushTime, (), (const, override)); - MOCK_METHOD(uint64_t, GetLastPingResponse, (), (const, override)); + MOCK_METHOD(uint64_t, GetLastReceivedTime, (), (const, override)); MOCK_METHOD(void, Disconnect, (std::string_view reason), (override)); }; diff --git a/ntcore/src/test/native/cpp/net/ServerImplTest.cpp b/ntcore/src/test/native/cpp/net/ServerImplTest.cpp index 64238111eed..b1fac0518c5 100644 --- a/ntcore/src/test/native/cpp/net/ServerImplTest.cpp +++ b/ntcore/src/test/native/cpp/net/ServerImplTest.cpp @@ -181,7 +181,7 @@ TEST_F(ServerImplTest, PublishLocal) { // EXPECT_CALL(wire, Flush()).WillOnce(Return(0)); // AddClient() EXPECT_CALL(setPeriodic, Call(100)); // ClientSubscribe() // EXPECT_CALL(wire, Flush()).WillOnce(Return(0)); // ClientSubscribe() - EXPECT_CALL(wire, GetLastPingResponse()).WillOnce(Return(0)); + EXPECT_CALL(wire, GetLastReceivedTime()).WillOnce(Return(0)); EXPECT_CALL(wire, SendPing(100)); EXPECT_CALL(wire, Ready()).WillOnce(Return(true)); // SendControl() EXPECT_CALL( @@ -258,7 +258,7 @@ TEST_F(ServerImplTest, ClientSubTopicOnlyThenValue) { // EXPECT_CALL(wire, Flush()).WillOnce(Return(0)); // AddClient() EXPECT_CALL(setPeriodic, Call(100)); // ClientSubscribe() // EXPECT_CALL(wire, Flush()).WillOnce(Return(0)); // ClientSubscribe() - EXPECT_CALL(wire, GetLastPingResponse()).WillOnce(Return(0)); + EXPECT_CALL(wire, GetLastReceivedTime()).WillOnce(Return(0)); EXPECT_CALL(wire, SendPing(100)); EXPECT_CALL(wire, Ready()).WillOnce(Return(true)); // SendValues() EXPECT_CALL( diff --git a/outlineviewer/publish.gradle b/outlineviewer/publish.gradle index 58222bed964..4cfb7bf14aa 100644 --- a/outlineviewer/publish.gradle +++ b/outlineviewer/publish.gradle @@ -29,51 +29,8 @@ model { def applicationPath = binary.executable.file def icon = file("$project.projectDir/src/main/native/mac/ov.icns") - // Create the macOS bundle. - def bundleTask = project.tasks.create("bundleOutlineViewerOsxApp" + binary.targetPlatform.architecture.name, Copy) { - description("Creates a macOS application bundle for OutlineViewer") - from(file("$project.projectDir/Info.plist")) - into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/OutlineViewer.app/Contents")) - into("MacOS") { - with copySpec { - from binary.executable.file - } - } - into("Resources") { - with copySpec { - from icon - } - } - - inputs.property "HasDeveloperId", project.hasProperty("developerID") - - doLast { - if (project.hasProperty("developerID")) { - // Get path to binary. - exec { - workingDir rootDir - def args = [ - "sh", - "-c", - "codesign --force --strict --deep " + - "--timestamp --options=runtime " + - "--verbose -s ${project.findProperty("developerID")} " + - "$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/OutlineViewer.app/" - ] - commandLine args - } - } - } - } - - // Reset the application path if we are creating a bundle. - if (binary.targetPlatform.operatingSystem.isMacOsX()) { - applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name") - project.build.dependsOn bundleTask - } - // Create the ZIP. - def task = project.tasks.create("copyOutlineViewerExecutable" + binary.targetPlatform.architecture.name, Zip) { + def task = project.tasks.create("copyOutlineViewerExecutable" + binary.targetPlatform.operatingSystem.name + binary.targetPlatform.architecture.name, Zip) { description("Copies the OutlineViewer executable to the outputs directory.") destinationDirectory = outputsFolder @@ -85,8 +42,6 @@ model { into '/' } - from(applicationPath) - if (binary.targetPlatform.operatingSystem.isWindows()) { def exePath = binary.executable.file.absolutePath exePath = exePath.substring(0, exePath.length() - 4) @@ -98,8 +53,52 @@ model { } if (binary.targetPlatform.operatingSystem.isMacOsX()) { + // Create the macOS bundle. + def bundleTask = project.tasks.create("bundleOutlineViewerOsxApp" + binary.targetPlatform.architecture.name, Copy) { + description("Creates a macOS application bundle for OutlineViewer") + from(file("$project.projectDir/Info.plist")) + into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/OutlineViewer.app/Contents")) + into("MacOS") { + with copySpec { + from binary.executable.file + } + } + into("Resources") { + with copySpec { + from icon + } + } + + inputs.property "HasDeveloperId", project.hasProperty("developerID") + + doLast { + if (project.hasProperty("developerID")) { + // Get path to binary. + exec { + workingDir rootDir + def args = [ + "sh", + "-c", + "codesign --force --strict --deep " + + "--timestamp --options=runtime " + + "--verbose -s ${project.findProperty("developerID")} " + + "$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/OutlineViewer.app/" + ] + commandLine args + } + } + } + } + + // Reset the application path if we are creating a bundle. + applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name") + task.from(applicationPath) + project.build.dependsOn bundleTask + bundleTask.dependsOn binary.tasks.link task.dependsOn(bundleTask) + } else { + task.from(applicationPath) } task.dependsOn binary.tasks.link diff --git a/outlineviewer/src/main/generate/WPILibVersion.cpp.in b/outlineviewer/src/main/generate/WPILibVersion.cpp.in index b0a44905207..cfe24411588 100644 --- a/outlineviewer/src/main/generate/WPILibVersion.cpp.in +++ b/outlineviewer/src/main/generate/WPILibVersion.cpp.in @@ -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. */ diff --git a/outlineviewer/src/main/native/cpp/main.cpp b/outlineviewer/src/main/native/cpp/main.cpp index 03107569fdf..36801ee3dac 100644 --- a/outlineviewer/src/main/native/cpp/main.cpp +++ b/outlineviewer/src/main/native/cpp/main.cpp @@ -62,7 +62,7 @@ static void NtInitialize() { auto inst = nt::GetDefaultInstance(); auto poller = nt::CreateListenerPoller(inst); nt::AddPolledListener(poller, inst, NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE); - nt::AddPolledLogger(poller, 0, 100); + nt::AddPolledLogger(poller, NT_LOG_INFO, 100); gui::AddEarlyExecute([inst, poller] { auto win = gui::GetSystemWindow(); if (!win) { diff --git a/roborioteamnumbersetter/publish.gradle b/roborioteamnumbersetter/publish.gradle index 824c2b91788..83d798d4061 100644 --- a/roborioteamnumbersetter/publish.gradle +++ b/roborioteamnumbersetter/publish.gradle @@ -42,8 +42,6 @@ model { into '/' } - from(applicationPath) - if (binary.targetPlatform.operatingSystem.isWindows()) { def exePath = binary.executable.file.absolutePath exePath = exePath.substring(0, exePath.length() - 4) @@ -94,10 +92,13 @@ model { // Reset the application path if we are creating a bundle. applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name") + task.from(applicationPath) project.build.dependsOn bundleTask bundleTask.dependsOn binary.tasks.link task.dependsOn(bundleTask) + } else { + task.from(applicationPath) } task.dependsOn binary.tasks.link diff --git a/roborioteamnumbersetter/src/main/generate/WPILibVersion.cpp.in b/roborioteamnumbersetter/src/main/generate/WPILibVersion.cpp.in index b0a44905207..cfe24411588 100644 --- a/roborioteamnumbersetter/src/main/generate/WPILibVersion.cpp.in +++ b/roborioteamnumbersetter/src/main/generate/WPILibVersion.cpp.in @@ -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. */ diff --git a/roborioteamnumbersetter/src/main/native/cpp/App.cpp b/roborioteamnumbersetter/src/main/native/cpp/App.cpp index eefb52b3a3d..d961fae5eff 100644 --- a/roborioteamnumbersetter/src/main/native/cpp/App.cpp +++ b/roborioteamnumbersetter/src/main/native/cpp/App.cpp @@ -57,6 +57,8 @@ struct TeamNumberRefHolder { static std::unique_ptr teamNumberRef; static std::unordered_map> foundDevices; +static std::unordered_map> + deviceStatuses; static wpi::Logger logger; static sysid::DeploySession deploySession{logger}; static std::unique_ptr multicastResolver; @@ -76,7 +78,12 @@ static void FindDevices() { [](const auto& a) { return a.first == "MAC"; }); if (macKey != data.txt.end()) { auto& mac = macKey->second; - foundDevices[mac] = std::make_pair(data.ipv4Address, data.hostName); + auto& foundDevice = foundDevices[mac]; + foundDevice = std::make_pair(data.ipv4Address, data.hostName); + auto& deviceStatus = deviceStatuses[mac]; + if (!deviceStatus) { + deploySession.GetStatus(mac, foundDevice.first); + } } } } @@ -146,15 +153,12 @@ static void DisplayGui() { int macWidth = ImGui::CalcTextSize("88:88:88:88:88:88").x; int ipAddressWidth = ImGui::CalcTextSize("255.255.255.255").x; int setWidth = ImGui::CalcTextSize(" Set Team To 99999 ").x; - int blinkWidth = ImGui::CalcTextSize(" Blink ").x; - int rebootWidth = ImGui::CalcTextSize(" Reboot ").x; - minWidth = nameWidth + macWidth + ipAddressWidth + setWidth + blinkWidth + - rebootWidth + 100; + minWidth = nameWidth + macWidth + ipAddressWidth + setWidth + 100; std::string setString = fmt::format("Set team to {}", teamNumber); - if (ImGui::BeginTable("Table", 6)) { + if (ImGui::BeginTable("Table", 4)) { ImGui::TableSetupColumn( "Name", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, @@ -171,17 +175,33 @@ static void DisplayGui() { "Set", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, setWidth); - ImGui::TableSetupColumn( - "Blink", - ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, - blinkWidth); - ImGui::TableSetupColumn( - "Reboot", - ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, - rebootWidth); ImGui::TableHeadersRow(); - for (auto&& i : foundDevices) { + ImGui::EndTable(); + } + + for (auto&& i : foundDevices) { + std::future* future = deploySession.GetFuture(i.first); + std::future* futureStatus = + deploySession.GetStatusFuture(i.first); + if (ImGui::BeginTable("Table", 4)) { + ImGui::TableSetupColumn( + "Name", + ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, + nameWidth); + ImGui::TableSetupColumn( + "MAC Address", + ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, + macWidth); + ImGui::TableSetupColumn( + "IP Address", + ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, + ipAddressWidth); + ImGui::TableSetupColumn( + "Set", + ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, + setWidth); + ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("%s", i.second.second.c_str()); @@ -192,11 +212,8 @@ static void DisplayGui() { in.s_addr = i.second.first; ImGui::Text("%s", inet_ntoa(in)); ImGui::TableNextColumn(); - std::future* future = deploySession.GetFuture(i.first); ImGui::PushID(i.first.c_str()); if (future) { - ImGui::Button("Deploying"); - ImGui::TableNextColumn(); ImGui::TableNextColumn(); const auto fs = future->wait_for(std::chrono::seconds(0)); if (fs == std::future_status::ready) { @@ -207,18 +224,62 @@ static void DisplayGui() { deploySession.ChangeTeamNumber(i.first, teamNumber, i.second.first); } ImGui::TableNextColumn(); - if (ImGui::Button("Blink")) { - deploySession.Blink(i.first, i.second.first); - } - ImGui::TableNextColumn(); - if (ImGui::Button("Reboot")) { - deploySession.Reboot(i.first, i.second.first); - } } + ImGui::PopID(); + ImGui::EndTable(); } - ImGui::EndTable(); + ImGui::PushID(i.first.c_str()); + if (futureStatus) { + ImGui::Text("Refreshing Status"); + const auto fs = futureStatus->wait_for(std::chrono::seconds(0)); + if (fs == std::future_status::ready) { + deviceStatuses[i.first] = futureStatus->get(); + deploySession.DestroyStatusFuture(i.first); + } + } else { + auto& deviceStatus = deviceStatuses[i.first]; + if (deviceStatus) { + if (ImGui::Button("Refresh Status")) { + deploySession.GetStatus(i.first, i.second.first); + } + std::string formatted = + fmt::format("Image: {}", deviceStatus.value().image); + ImGui::Text("%s", formatted.c_str()); + formatted = fmt::format("Serial Number: {}", + deviceStatus.value().serialNumber); + ImGui::Text("%s", formatted.c_str()); + formatted = fmt::format( + "Web Server Status: {}", + deviceStatus.value().webServerEnabled ? "Enabled" : "Disabled"); + ImGui::Text("%s", formatted.c_str()); + } else { + ImGui::Text("Waiting for refresh"); + } + } + + if (future) { + ImGui::Text("Deploying"); + } else { + if (ImGui::Button("Blink")) { + deploySession.Blink(i.first, i.second.first); + } + ImGui::SameLine(); + if (ImGui::Button("Reboot")) { + deploySession.Reboot(i.first, i.second.first); + } + ImGui::SameLine(); + if (ImGui::Button("Disable Web Server")) { + deploySession.DisableWebServer(i.first, i.second.first); + } + ImGui::SameLine(); + if (ImGui::Button("Enable Web Server")) { + deploySession.EnableWebServer(i.first, i.second.first); + } + } + ImGui::Separator(); + ImGui::PopID(); } ImGui::Columns(6, "Devices"); diff --git a/roborioteamnumbersetter/src/main/native/cpp/DeploySession.cpp b/roborioteamnumbersetter/src/main/native/cpp/DeploySession.cpp index 3fd63cd7129..48fa19852db 100644 --- a/roborioteamnumbersetter/src/main/native/cpp/DeploySession.cpp +++ b/roborioteamnumbersetter/src/main/native/cpp/DeploySession.cpp @@ -37,6 +37,7 @@ static constexpr std::string_view kUsername = "admin"; static constexpr std::string_view kPassword = ""; std::unordered_map> s_outstanding; +std::unordered_map> s_outstandingStatus; DeploySession::DeploySession(wpi::Logger& logger) : m_logger{logger} {} @@ -59,6 +60,19 @@ void DeploySession::DestroyFuture(const std::string& macAddress) { s_outstanding.erase(macAddress); } +std::future* DeploySession::GetStatusFuture( + const std::string& macAddress) { + auto itr = s_outstandingStatus.find(macAddress); + if (itr == s_outstandingStatus.end()) { + return nullptr; + } + return &itr->second; +} + +void DeploySession::DestroyStatusFuture(const std::string& macAddress) { + s_outstandingStatus.erase(macAddress); +} + bool DeploySession::ChangeTeamNumber(const std::string& macAddress, int teamNumber, unsigned int ipAddress) { auto itr = s_outstanding.find(macAddress); @@ -184,3 +198,147 @@ bool DeploySession::Blink(const std::string& macAddress, s_outstanding[macAddress] = std::move(future); return true; } + +bool DeploySession::DisableWebServer(const std::string& macAddress, + unsigned int ipAddress) { + auto itr = s_outstanding.find(macAddress); + if (itr != s_outstanding.end()) { + return false; + } + + std::future future = + std::async(std::launch::async, [this, ipAddress, mac = macAddress]() { + // Convert to IP address. + wpi::SmallString<16> ip; + in_addr addr; + addr.s_addr = ipAddress; + wpi::uv::AddrToName(addr, &ip); + DEBUG("Trying to establish SSH connection to {}.", ip.str()); + try { + SshSession session{ip.str(), kPort, kUsername, kPassword, m_logger}; + session.Open(); + DEBUG("SSH connection to {} was successful.", ip.str()); + + SUCCESS("roboRIO Connected!"); + + try { + session.Execute( + "/bin/bash -c \"/etc/init.d/systemWebServer stop\""); + session.Execute( + "/bin/bash -c \"update-rc.d -f systemWebServer remove\""); + session.Execute("/bin/bash -c \"sync\""); + } catch (const SshSession::SshException& e) { + ERROR("An exception occurred: {}", e.what()); + throw e; + } + } catch (const SshSession::SshException& e) { + DEBUG("SSH connection to {} failed with {}.", ip.str(), e.what()); + throw e; + } + return 0; + }); + + s_outstanding[macAddress] = std::move(future); + return true; +} + +bool DeploySession::EnableWebServer(const std::string& macAddress, + unsigned int ipAddress) { + auto itr = s_outstanding.find(macAddress); + if (itr != s_outstanding.end()) { + return false; + } + + std::future future = + std::async(std::launch::async, [this, ipAddress, mac = macAddress]() { + // Convert to IP address. + wpi::SmallString<16> ip; + in_addr addr; + addr.s_addr = ipAddress; + wpi::uv::AddrToName(addr, &ip); + DEBUG("Trying to establish SSH connection to {}.", ip.str()); + try { + SshSession session{ip.str(), kPort, kUsername, kPassword, m_logger}; + session.Open(); + DEBUG("SSH connection to {} was successful.", ip.str()); + + SUCCESS("roboRIO Connected!"); + + try { + session.Execute( + "/bin/bash -c \"update-rc.d -f systemWebServer defaults\""); + session.Execute( + "/bin/bash -c \"/etc/init.d/systemWebServer start\""); + session.Execute("/bin/bash -c \"sync\""); + } catch (const SshSession::SshException& e) { + ERROR("An exception occurred: {}", e.what()); + throw e; + } + } catch (const SshSession::SshException& e) { + DEBUG("SSH connection to {} failed with {}.", ip.str(), e.what()); + throw e; + } + return 0; + }); + + s_outstanding[macAddress] = std::move(future); + return true; +} + +bool DeploySession::GetStatus(const std::string& macAddress, + unsigned int ipAddress) { + auto itr = s_outstandingStatus.find(macAddress); + if (itr != s_outstandingStatus.end()) { + return false; + } + + std::future future = + std::async(std::launch::async, [this, ipAddress, mac = macAddress]() { + // Convert to IP address. + wpi::SmallString<16> ip; + in_addr addr; + addr.s_addr = ipAddress; + wpi::uv::AddrToName(addr, &ip); + DEBUG("Trying to establish SSH connection to {}.", ip.str()); + DeviceStatus status; + try { + SshSession session{ip.str(), kPort, kUsername, kPassword, m_logger}; + session.Open(); + DEBUG("SSH connection to {} was successful.", ip.str()); + + SUCCESS("roboRIO Connected!"); + + try { + int exitStatus = 0; + session.ExecuteResult( + "start-stop-daemon --status -x " + "/usr/local/natinst/share/NIWebServer/SystemWebServer", + &exitStatus); + status.webServerEnabled = exitStatus == 0; + auto serialNumber = session.ExecuteResult( + "/sbin/fw_printenv -n serial#", &exitStatus); + if (exitStatus == 0) { + status.serialNumber = wpi::trim(serialNumber); + } + auto image = session.ExecuteResult( + "/usr/local/natinst/bin/nirtcfg --file " + "/etc/natinst/share/scs_imagemetadata.ini --get " + "section=ImageMetadata,token=IMAGEVERSION,value=UNKNOWN", + &exitStatus); + if (exitStatus == 0) { + status.image = wpi::trim(image); + } + } catch (const SshSession::SshException& e) { + ERROR("An exception occurred: {}", e.what()); + throw e; + } + } catch (const SshSession::SshException& e) { + DEBUG("SSH connection to {} failed with {}.", ip.str(), e.what()); + throw e; + } + return status; + }); + + s_outstandingStatus[macAddress] = std::move(future); + return true; +} diff --git a/roborioteamnumbersetter/src/main/native/cpp/DeploySession.h b/roborioteamnumbersetter/src/main/native/cpp/DeploySession.h index 7cc5bc78535..5586e9826c1 100644 --- a/roborioteamnumbersetter/src/main/native/cpp/DeploySession.h +++ b/roborioteamnumbersetter/src/main/native/cpp/DeploySession.h @@ -18,6 +18,12 @@ namespace sysid { // GUI). static constexpr unsigned int kLogSuccess = 31; +struct DeviceStatus { + bool webServerEnabled = false; + std::string serialNumber; + std::string image; +}; + /** * Represents a single deploy session. * @@ -49,15 +55,19 @@ class DeploySession { bool Blink(const std::string& macAddress, unsigned int ipAddress); + bool DisableWebServer(const std::string& macAddress, unsigned int ipAddress); + + bool EnableWebServer(const std::string& macAddress, unsigned int ipAddress); + bool Reboot(const std::string& macAddress, unsigned int ipAddress); + bool GetStatus(const std::string& macAddress, unsigned int ipAddress); + std::future* GetFuture(const std::string& macAddress); void DestroyFuture(const std::string& macAddress); - /** - * Returns the state of the deploy session. - */ - Status GetStatus() const; + std::future* GetStatusFuture(const std::string& macAddress); + void DestroyStatusFuture(const std::string& macAddress); private: // Logger reference where log messages will be sent. diff --git a/roborioteamnumbersetter/src/main/native/cpp/SshSession.cpp b/roborioteamnumbersetter/src/main/native/cpp/SshSession.cpp index 74fc6385b89..95bbb158015 100644 --- a/roborioteamnumbersetter/src/main/native/cpp/SshSession.cpp +++ b/roborioteamnumbersetter/src/main/native/cpp/SshSession.cpp @@ -86,13 +86,28 @@ void SshSession::Execute(std::string_view cmd) { ssh_channel_free(channel); throw SshException(ssh_get_error(m_session)); } - INFO("{}", cmd); + INFO("{} {}", ssh_channel_get_exit_status(channel), cmd); // Log output. char buf[512]; int read = ssh_channel_read(channel, buf, sizeof(buf), 0); if (read != 0) { - INFO("{}", cmd); + if (read < static_cast(sizeof(buf) / sizeof(buf[0]))) { + buf[read] = 0; + } else { + buf[(sizeof(buf) / sizeof(buf[0])) - 1] = 0; + } + INFO("stdout: {} {}", read, buf); + } + + read = ssh_channel_read(channel, buf, sizeof(buf), 1); + if (read != 0) { + if (read < static_cast(sizeof(buf) / sizeof(buf[0]))) { + buf[read] = 0; + } else { + buf[(sizeof(buf) / sizeof(buf[0])) - 1] = 0; + } + INFO("stderr: {} {}", read, buf); } // Close and free channel. @@ -100,6 +115,64 @@ void SshSession::Execute(std::string_view cmd) { ssh_channel_free(channel); } +std::string SshSession::ExecuteResult(std::string_view cmd, int* exitStatus) { + // Allocate a new channel. + ssh_channel channel = ssh_channel_new(m_session); + if (!channel) { + throw SshException(ssh_get_error(m_session)); + } + + // Open the channel. + int rc = ssh_channel_open_session(channel); + if (rc != SSH_OK) { + throw SshException(ssh_get_error(m_session)); + } + + // Execute the command. + std::string command{cmd}; + rc = ssh_channel_request_exec(channel, command.c_str()); + if (rc != SSH_OK) { + ssh_channel_close(channel); + ssh_channel_free(channel); + throw SshException(ssh_get_error(m_session)); + } + INFO("{} {}", ssh_channel_get_exit_status(channel), cmd); + + std::string result; + if (exitStatus) { + *exitStatus = ssh_channel_get_exit_status(channel); + } + + // Log output. + char buf[512]; + int read = ssh_channel_read(channel, buf, sizeof(buf), 0); + if (read != 0) { + if (read < static_cast(sizeof(buf) / sizeof(buf[0]))) { + buf[read] = 0; + } else { + buf[(sizeof(buf) / sizeof(buf[0])) - 1] = 0; + } + result = buf; + INFO("stdout: {} {}", read, buf); + } + + read = ssh_channel_read(channel, buf, sizeof(buf), 1); + if (read != 0) { + if (read < static_cast(sizeof(buf) / sizeof(buf[0]))) { + buf[read] = 0; + } else { + buf[(sizeof(buf) / sizeof(buf[0])) - 1] = 0; + } + INFO("stderr: {} {}", read, buf); + } + + // Close and free channel. + ssh_channel_close(channel); + ssh_channel_free(channel); + + return result; +} + void SshSession::Put(std::string_view path, std::string_view contents) { // Allocate the SFTP session. sftp_session sftp = sftp_new(m_session); diff --git a/roborioteamnumbersetter/src/main/native/cpp/SshSession.h b/roborioteamnumbersetter/src/main/native/cpp/SshSession.h index 47db332dad2..df91a07f65f 100644 --- a/roborioteamnumbersetter/src/main/native/cpp/SshSession.h +++ b/roborioteamnumbersetter/src/main/native/cpp/SshSession.h @@ -58,6 +58,8 @@ class SshSession { */ void Execute(std::string_view cmd); + std::string ExecuteResult(std::string_view cmd, int* exitStatus); + /** * Puts a file on the server using SFTP. * diff --git a/roborioteamnumbersetter/src/main/native/cpp/main.cpp b/roborioteamnumbersetter/src/main/native/cpp/main.cpp index 5f1261b00f4..3d1a1965f29 100644 --- a/roborioteamnumbersetter/src/main/native/cpp/main.cpp +++ b/roborioteamnumbersetter/src/main/native/cpp/main.cpp @@ -1,25 +1,25 @@ -// 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 - -void Application(std::string_view saveDir); - -#ifdef _WIN32 -int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* pCmdLine, - int nCmdShow) { - int argc = __argc; - char** argv = __argv; -#else -int main(int argc, char** argv) { -#endif - std::string_view saveDir; - if (argc == 2) { - saveDir = argv[1]; - } - - Application(saveDir); - - return 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 + +void Application(std::string_view saveDir); + +#ifdef _WIN32 +int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* pCmdLine, + int nCmdShow) { + int argc = __argc; + char** argv = __argv; +#else +int main(int argc, char** argv) { +#endif + std::string_view saveDir; + if (argc == 2) { + saveDir = argv[1]; + } + + Application(saveDir); + + return 0; +} diff --git a/romiVendordep/CMakeLists.txt b/romiVendordep/CMakeLists.txt index 7ef8bde3370..67cb5405bb9 100644 --- a/romiVendordep/CMakeLists.txt +++ b/romiVendordep/CMakeLists.txt @@ -66,12 +66,12 @@ target_include_directories( $ ) -install(TARGETS romiVendordep EXPORT romiVendordep DESTINATION "${main_lib_dest}") +install(TARGETS romiVendordep EXPORT romivendordep DESTINATION "${main_lib_dest}") install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/romiVendordep") -configure_file(romiVendordep-config.cmake.in ${WPILIB_BINARY_DIR}/romiVendordep-config.cmake) -install(FILES ${WPILIB_BINARY_DIR}/romiVendordep-config.cmake DESTINATION share/romiVendordep) -install(EXPORT romiVendordep DESTINATION share/romiVendordep) +configure_file(romivendordep-config.cmake.in ${WPILIB_BINARY_DIR}/romivendordep-config.cmake) +install(FILES ${WPILIB_BINARY_DIR}/romivendordep-config.cmake DESTINATION share/romiVendordep) +install(EXPORT romivendordep DESTINATION share/romiVendordep) if(WITH_TESTS) wpilib_add_test(romiVendordep src/test/native/cpp) diff --git a/romiVendordep/romiVendordep-config.cmake.in b/romiVendordep/romiVendordep-config.cmake.in deleted file mode 100644 index 2843d3e728a..00000000000 --- a/romiVendordep/romiVendordep-config.cmake.in +++ /dev/null @@ -1,14 +0,0 @@ -include(CMakeFindDependencyMacro) - @WPIUTIL_DEP_REPLACE@ - @NTCORE_DEP_REPLACE@ - @CSCORE_DEP_REPLACE@ - @CAMERASERVER_DEP_REPLACE@ - @HAL_DEP_REPLACE@ - @WPILIBC_DEP_REPLACE@ - @WPIMATH_DEP_REPLACE@ - - @FILENAME_DEP_REPLACE@ - include(${SELF_DIR}/romiVendordep.cmake) - if(@WITH_JAVA@) - include(${SELF_DIR}/romiVendordep_jar.cmake) - endif() diff --git a/romiVendordep/romivendordep-config.cmake.in b/romiVendordep/romivendordep-config.cmake.in new file mode 100644 index 00000000000..d4a80f0d6ae --- /dev/null +++ b/romiVendordep/romivendordep-config.cmake.in @@ -0,0 +1,14 @@ +include(CMakeFindDependencyMacro) +@WPIUTIL_DEP_REPLACE@ +@NTCORE_DEP_REPLACE@ +@CSCORE_DEP_REPLACE@ +@CAMERASERVER_DEP_REPLACE@ +@HAL_DEP_REPLACE@ +@WPILIBC_DEP_REPLACE@ +@WPIMATH_DEP_REPLACE@ + +@FILENAME_DEP_REPLACE@ +include(${SELF_DIR}/romivendordep.cmake) +if(@WITH_JAVA@) + include(${SELF_DIR}/romiVendordep_jar.cmake) +endif() diff --git a/romiVendordep/src/test/native/cpp/main.cpp b/romiVendordep/src/test/native/cpp/main.cpp index 2d710be58f7..a2b90c59137 100644 --- a/romiVendordep/src/test/native/cpp/main.cpp +++ b/romiVendordep/src/test/native/cpp/main.cpp @@ -1,10 +1,10 @@ -// 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 - -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} +// 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 + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/simulation/halsim_gui/src/main/native/cpp/DriverStationGui.cpp b/simulation/halsim_gui/src/main/native/cpp/DriverStationGui.cpp index 09192f39fff..42df53efea4 100644 --- a/simulation/halsim_gui/src/main/native/cpp/DriverStationGui.cpp +++ b/simulation/halsim_gui/src/main/native/cpp/DriverStationGui.cpp @@ -227,37 +227,25 @@ class FMSSimModel : public glass::FMSModel { glass::DataSource* GetAutonomousData() override { return &m_autonomous; } std::string_view GetGameSpecificMessage( wpi::SmallVectorImpl& buf) override { - HAL_MatchInfo info; - HALSIM_GetMatchInfo(&info); - buf.clear(); - buf.append(info.gameSpecificMessage, - info.gameSpecificMessage + info.gameSpecificMessageSize); - return std::string_view(buf.begin(), buf.size()); + return m_gameMessage; } - void SetFmsAttached(bool val) override { - HALSIM_SetDriverStationFmsAttached(val); - } - void SetDsAttached(bool val) override { - HALSIM_SetDriverStationDsAttached(val); - } + void SetFmsAttached(bool val) override { m_fmsAttached.SetValue(val); } + void SetDsAttached(bool val) override { m_dsAttached.SetValue(val); } void SetAllianceStationId(int val) override { - HALSIM_SetDriverStationAllianceStationId( - static_cast(val)); - } - void SetMatchTime(double val) override { - HALSIM_SetDriverStationMatchTime(val); - } - void SetEStop(bool val) override { HALSIM_SetDriverStationEStop(val); } - void SetEnabled(bool val) override { HALSIM_SetDriverStationEnabled(val); } - void SetTest(bool val) override { HALSIM_SetDriverStationTest(val); } - void SetAutonomous(bool val) override { - HALSIM_SetDriverStationAutonomous(val); + m_allianceStationId.SetValue(val); } + void SetMatchTime(double val) override { m_matchTime.SetValue(val); } + void SetEStop(bool val) override { m_estop.SetValue(val); } + void SetEnabled(bool val) override { m_enabled.SetValue(val); } + void SetTest(bool val) override { m_test.SetValue(val); } + void SetAutonomous(bool val) override { m_autonomous.SetValue(val); } void SetGameSpecificMessage(std::string_view val) override { - HALSIM_SetGameSpecificMessage(val.data(), val.size()); + m_gameMessage = val; } + void UpdateHAL(); + void Update() override; bool Exists() override { return true; } @@ -274,6 +262,7 @@ class FMSSimModel : public glass::FMSModel { glass::DataSource m_test{"FMS:TestMode"}; glass::DataSource m_autonomous{"FMS:AutonomousMode"}; double m_startMatchTime = -1.0; + std::string m_gameMessage; }; } // namespace @@ -1019,6 +1008,21 @@ void RobotJoystick::GetHAL(int i) { HALSIM_GetJoystickPOVs(i, &data.povs); } +static void DriverStationConnect(bool enabled, bool autonomous, bool test) { + if (!HALSIM_GetDriverStationDsAttached()) { + // initialize FMS bits too + gFMSModel->SetDsAttached(true); + gFMSModel->SetEnabled(enabled); + gFMSModel->SetAutonomous(autonomous); + gFMSModel->SetTest(test); + gFMSModel->UpdateHAL(); + } else { + HALSIM_SetDriverStationEnabled(enabled); + HALSIM_SetDriverStationAutonomous(autonomous); + HALSIM_SetDriverStationTest(test); + } +} + static void DriverStationExecute() { // update sources for (int i = 0; i < HAL_kMaxJoysticks; ++i) { @@ -1072,6 +1076,7 @@ static void DriverStationExecute() { joy.Update(); } + bool isAttached = HALSIM_GetDriverStationDsAttached(); bool isEnabled = HALSIM_GetDriverStationEnabled(); bool isAuto = HALSIM_GetDriverStationAutonomous(); bool isTest = HALSIM_GetDriverStationTest(); @@ -1100,38 +1105,45 @@ static void DriverStationExecute() { ImGui::SetNextWindowPos(ImVec2{5, 20}, ImGuiCond_FirstUseEver); ImGui::Begin("Robot State", nullptr, ImGuiWindowFlags_AlwaysAutoResize); - if (ImGui::Selectable("Disabled", !isEnabled) || disableHotkey) { + if (ImGui::Selectable("Disconnected", !isAttached)) { HALSIM_SetDriverStationEnabled(false); + HALSIM_SetDriverStationDsAttached(false); + isAttached = false; + gFMSModel->Update(); + } + if (ImGui::Selectable("Disabled", isAttached && !isEnabled) || + disableHotkey) { + DriverStationConnect(false, false, false); } - if (ImGui::Selectable("Autonomous", isEnabled && isAuto && !isTest)) { - HALSIM_SetDriverStationAutonomous(true); - HALSIM_SetDriverStationTest(false); - HALSIM_SetDriverStationEnabled(true); + if (ImGui::Selectable("Autonomous", + isAttached && isEnabled && isAuto && !isTest)) { + DriverStationConnect(true, true, false); } - if (ImGui::Selectable("Teleoperated", isEnabled && !isAuto && !isTest) || + if (ImGui::Selectable("Teleoperated", + isAttached && isEnabled && !isAuto && !isTest) || enableHotkey) { - HALSIM_SetDriverStationAutonomous(false); - HALSIM_SetDriverStationTest(false); - HALSIM_SetDriverStationEnabled(true); + DriverStationConnect(true, false, false); } if (ImGui::Selectable("Test", isEnabled && isTest)) { - HALSIM_SetDriverStationAutonomous(false); - HALSIM_SetDriverStationTest(true); - HALSIM_SetDriverStationEnabled(true); + DriverStationConnect(true, false, true); } ImGui::End(); } // Update HAL - for (int i = 0, end = gRobotJoysticks.size(); - i < end && i < HAL_kMaxJoysticks; ++i) { - gRobotJoysticks[i].SetHAL(i); + if (isAttached) { + for (int i = 0, end = gRobotJoysticks.size(); + i < end && i < HAL_kMaxJoysticks; ++i) { + gRobotJoysticks[i].SetHAL(i); + } } // Send new data every 20 ms (may be slower depending on GUI refresh rate) static double lastNewDataTime = 0.0; - if ((curTime - lastNewDataTime) > 0.02 && !HALSIM_IsTimingPaused()) { + if ((curTime - lastNewDataTime) > 0.02 && !HALSIM_IsTimingPaused() && + isAttached) { lastNewDataTime = curTime; + gFMSModel->Update(); HALSIM_NotifyDriverStationNewData(); } } @@ -1144,6 +1156,20 @@ FMSSimModel::FMSSimModel() { m_test.SetDigital(true); m_autonomous.SetDigital(true); m_matchTime.SetValue(-1.0); + m_allianceStationId.SetValue(HAL_AllianceStationID_kRed1); +} + +void FMSSimModel::UpdateHAL() { + HALSIM_SetDriverStationFmsAttached(m_fmsAttached.GetValue()); + HALSIM_SetDriverStationAllianceStationId( + static_cast(m_allianceStationId.GetValue())); + HALSIM_SetDriverStationEStop(m_estop.GetValue()); + HALSIM_SetDriverStationEnabled(m_enabled.GetValue()); + HALSIM_SetDriverStationTest(m_test.GetValue()); + HALSIM_SetDriverStationAutonomous(m_autonomous.GetValue()); + HALSIM_SetDriverStationMatchTime(m_matchTime.GetValue()); + HALSIM_SetGameSpecificMessage(m_gameMessage.data(), m_gameMessage.size()); + HALSIM_SetDriverStationDsAttached(m_dsAttached.GetValue()); } void FMSSimModel::Update() { @@ -1176,6 +1202,11 @@ void FMSSimModel::Update() { m_startMatchTime = -1.0; } m_matchTime.SetValue(matchTime); + + HAL_MatchInfo info; + HALSIM_GetMatchInfo(&info); + m_gameMessage.assign(info.gameSpecificMessage, + info.gameSpecificMessage + info.gameSpecificMessageSize); } bool FMSSimModel::IsReadOnly() { @@ -1387,7 +1418,6 @@ void DriverStationGui::GlobalInit() { gFMSModel = std::make_unique(); wpi::gui::AddEarlyExecute(DriverStationExecute); - wpi::gui::AddEarlyExecute([] { gFMSModel->Update(); }); storageRoot.SetCustomApply([&storageRoot] { gpDisableDS = &storageRoot.GetBool("disable", false); @@ -1433,8 +1463,13 @@ void DriverStationGui::GlobalInit() { win->SetDefaultSize(300, 560); } } - if (auto win = - dsManager->AddWindow("FMS", [] { DisplayFMS(gFMSModel.get()); })) { + if (auto win = dsManager->AddWindow("FMS", [] { + if (HALSIM_GetDriverStationDsAttached()) { + DisplayFMSReadOnly(gFMSModel.get()); + } else { + DisplayFMS(gFMSModel.get(), false); + } + })) { win->DisableRenamePopup(); win->SetFlags(ImGuiWindowFlags_AlwaysAutoResize); win->SetDefaultPos(5, 540); diff --git a/styleguide/spotbugs-exclude.xml b/styleguide/spotbugs-exclude.xml index 9bee5c524d7..ba4feb2ffd6 100644 --- a/styleguide/spotbugs-exclude.xml +++ b/styleguide/spotbugs-exclude.xml @@ -138,6 +138,10 @@ + + + + diff --git a/sysid/README.md b/sysid/README.md new file mode 100644 index 00000000000..842f3bec684 --- /dev/null +++ b/sysid/README.md @@ -0,0 +1,14 @@ +# SysId: System Identification for Robot Mechanisms + +## Building and Running SysId + +See [here](../README.md#Requirements) for build requirements. + +Run the following in the monorepo root. +```bash +./gradlew sysid:run +``` + +## Troubleshooting + +Use [AdvantageScope](https://docs.wpilib.org/en/stable/docs/software/dashboards/advantagescope.html) (shipped with the WPILib installer) to view .wpilog files for troubleshooting when SysId fails to generate plots. diff --git a/sysid/build.gradle b/sysid/build.gradle index e6709fc8285..b9689e96b35 100644 --- a/sysid/build.gradle +++ b/sysid/build.gradle @@ -102,8 +102,8 @@ model { lib project: ':glass', library: 'glass', linkage: 'static' project(':ntcore').addNtcoreDependency(it, 'static') lib project: ':wpinet', library: 'wpinet', linkage: 'static' - lib project: ':wpiutil', library: 'wpiutil', linkage: 'static' lib project: ':wpimath', library: 'wpimath', linkage: 'static' + lib project: ':wpiutil', library: 'wpiutil', linkage: 'static' lib project: ':wpigui', library: 'wpigui', linkage: 'static' nativeUtils.useRequiredLibrary(it, 'imgui') if (it.targetPlatform.operatingSystem.isWindows()) { @@ -144,8 +144,8 @@ model { lib project: ':glass', library: 'glass', linkage: 'static' project(':ntcore').addNtcoreDependency(it, 'static') lib project: ':wpinet', library: 'wpinet', linkage: 'static' - lib project: ':wpiutil', library: 'wpiutil', linkage: 'static' lib project: ':wpimath', library: 'wpimath', linkage: 'static' + lib project: ':wpiutil', library: 'wpiutil', linkage: 'static' lib project: ':wpigui', library: 'wpigui', linkage: 'static' nativeUtils.useRequiredLibrary(it, 'imgui') if (it.targetPlatform.operatingSystem.isWindows()) { diff --git a/sysid/scripts/time_plots.py b/sysid/scripts/time_plots.py deleted file mode 100755 index 6878126af2c..00000000000 --- a/sysid/scripts/time_plots.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env python3 - -import json -import pathlib - -import matplotlib.pyplot as plt -import pandas as pd -import sys - -# Load data -filename = pathlib.Path(sys.argv[1]) - -UNIT_TO_ABBREVIATION = { - "Meters": "m", - "Feet": "ft", - "Inches": "in", - "Degrees": "deg", - "Rotations": "rot", - "Radians": "rad", -} - -# Make DataFrame to facilitate plotting -if filename.suffix == ".json": - raw_data = json.loads(filename.read_text()) - unit = raw_data["units"] - - # Get Unit - try: - abbreviation = UNIT_TO_ABBREVIATION[unit] - except KeyError: - raise ValueError("Invalid Unit") - - # Make Columns - columns = ["Timestamp (s)", "Test"] - if "Drive" in raw_data["test"]: - columns.extend( - [ - "Left Volts (V)", - "Right Volts (V)", - f"Left Position ({abbreviation})", - f"Right Position ({abbreviation})", - f"Left Velocity ({abbreviation}/s)", - f"Right Velocity ({abbreviation}/s)", - "Gyro Position (deg)", - "Gyro Rate (deg/s)", - ] - ) - unit_columns = columns[4:8] - else: - columns.extend( - ["Volts (V)", f"Position ({abbreviation})", f"Velocity ({abbreviation}/s)"] - ) - unit_columns = columns[3:] - - prepared_data = pd.DataFrame(columns=columns) - for test in raw_data.keys(): - if "-" not in test: - continue - formatted_entry = [[pt[0], test, *pt[1:]] for pt in raw_data[test]] - prepared_data = pd.concat( - [prepared_data, pd.DataFrame(formatted_entry, columns=columns)] - ) - - units_per_rot = raw_data["unitsPerRotation"] - - for column in unit_columns: - prepared_data[column] *= units_per_rot -else: - prepared_data = pd.read_csv(filename) - -# First 2 columns are Timestamp and Test -for column in prepared_data.columns[2:]: - # Configure Plot Labels - plt.figure() - plt.xlabel("Timestamp (s)") - plt.ylabel(column) - - # Configure title without units - print(column) - end = column.find("(") - plt.title(f"{column[:end].strip()} vs Time") - - # Plot data for each test - for test in pd.unique(prepared_data["Test"]): - test_data = prepared_data[prepared_data["Test"] == test] - plt.plot(test_data["Timestamp (s)"], test_data[column], label=test) - plt.legend() - -plt.show() diff --git a/sysid/src/main/generate/WPILibVersion.cpp.in b/sysid/src/main/generate/WPILibVersion.cpp.in index b0a44905207..cfe24411588 100644 --- a/sysid/src/main/generate/WPILibVersion.cpp.in +++ b/sysid/src/main/generate/WPILibVersion.cpp.in @@ -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. */ diff --git a/sysid/src/main/native/cpp/App.cpp b/sysid/src/main/native/cpp/App.cpp index 947ea434a65..4a60f79b9af 100644 --- a/sysid/src/main/native/cpp/App.cpp +++ b/sysid/src/main/native/cpp/App.cpp @@ -24,21 +24,20 @@ #include #include "sysid/view/Analyzer.h" -#include "sysid/view/JSONConverter.h" -#include "sysid/view/Logger.h" +#include "sysid/view/DataSelector.h" +#include "sysid/view/LogLoader.h" #include "sysid/view/UILayout.h" namespace gui = wpi::gui; static std::unique_ptr gWindowManager; -glass::Window* gLoggerWindow; +glass::Window* gLogLoaderWindow; +glass::Window* gDataSelectorWindow; glass::Window* gAnalyzerWindow; glass::Window* gProgramLogWindow; static glass::MainMenuBar gMainMenu; -std::unique_ptr gJSONConverter; - glass::LogData gLog; wpi::Logger gLogger; @@ -103,11 +102,23 @@ void Application(std::string_view saveDir) { gWindowManager = std::make_unique(storage); gWindowManager->GlobalInit(); - gLoggerWindow = gWindowManager->AddWindow( - "Logger", std::make_unique(storage, gLogger)); + auto logLoader = std::make_unique(storage, gLogger); + auto dataSelector = std::make_unique(storage, gLogger); + auto analyzer = std::make_unique(storage, gLogger); + + logLoader->unload.connect([ds = dataSelector.get()] { ds->Reset(); }); + dataSelector->testdata = [_analyzer = analyzer.get()](auto data) { + _analyzer->m_data = data; + _analyzer->AnalyzeData(); + }; + + gLogLoaderWindow = + gWindowManager->AddWindow("Log Loader", std::move(logLoader)); - gAnalyzerWindow = gWindowManager->AddWindow( - "Analyzer", std::make_unique(storage, gLogger)); + gDataSelectorWindow = + gWindowManager->AddWindow("Data Selector", std::move(dataSelector)); + + gAnalyzerWindow = gWindowManager->AddWindow("Analyzer", std::move(analyzer)); gProgramLogWindow = gWindowManager->AddWindow( "Program Log", std::make_unique(&gLog)); @@ -115,10 +126,16 @@ void Application(std::string_view saveDir) { // Set default positions and sizes for windows. // Logger window position/size - gLoggerWindow->SetDefaultPos(sysid::kLoggerWindowPos.x, - sysid::kLoggerWindowPos.y); - gLoggerWindow->SetDefaultSize(sysid::kLoggerWindowSize.x, - sysid::kLoggerWindowSize.y); + gLogLoaderWindow->SetDefaultPos(sysid::kLogLoaderWindowPos.x, + sysid::kLogLoaderWindowPos.y); + gLogLoaderWindow->SetDefaultSize(sysid::kLogLoaderWindowSize.x, + sysid::kLogLoaderWindowSize.y); + + // Data selector window position/size + gDataSelectorWindow->SetDefaultPos(sysid::kDataSelectorWindowPos.x, + sysid::kDataSelectorWindowPos.y); + gDataSelectorWindow->SetDefaultSize(sysid::kDataSelectorWindowSize.x, + sysid::kDataSelectorWindowSize.y); // Analyzer window position/size gAnalyzerWindow->SetDefaultPos(sysid::kAnalyzerWindowPos.x, @@ -133,8 +150,6 @@ void Application(std::string_view saveDir) { sysid::kProgramLogWindowSize.y); gProgramLogWindow->DisableRenamePopup(); - gJSONConverter = std::make_unique(gLogger); - // Configure save file. gui::ConfigurePlatformSaveFile("sysid.ini"); @@ -157,15 +172,6 @@ void Application(std::string_view saveDir) { ImGui::EndMenu(); } - bool toCSV = false; - if (ImGui::BeginMenu("JSON Converters")) { - if (ImGui::MenuItem("JSON to CSV Converter")) { - toCSV = true; - } - - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Docs")) { if (ImGui::MenuItem("Online documentation")) { wpi::gui::OpenURL( @@ -178,19 +184,6 @@ void Application(std::string_view saveDir) { ImGui::EndMainMenuBar(); - if (toCSV) { - ImGui::OpenPopup("SysId JSON to CSV Converter"); - toCSV = false; - } - - if (ImGui::BeginPopupModal("SysId JSON to CSV Converter")) { - gJSONConverter->DisplayCSVConvert(); - if (ImGui::Button("Close")) { - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } - if (about) { ImGui::OpenPopup("About"); about = false; diff --git a/sysid/src/main/native/cpp/Util.cpp b/sysid/src/main/native/cpp/Util.cpp index a9acc537a7a..c4dc7803046 100644 --- a/sysid/src/main/native/cpp/Util.cpp +++ b/sysid/src/main/native/cpp/Util.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -23,6 +24,20 @@ void sysid::CreateTooltip(const char* text) { } } +void sysid::CreateErrorTooltip(const char* text) { + ImGui::SameLine(); + ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), + ICON_FA_TRIANGLE_EXCLAMATION); + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%s", text); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} + void sysid::CreateErrorPopup(bool& isError, std::string_view errorMessage) { if (isError) { ImGui::OpenPopup("Exception Caught!"); diff --git a/sysid/src/main/native/cpp/analysis/AnalysisManager.cpp b/sysid/src/main/native/cpp/analysis/AnalysisManager.cpp index 8c1ffff6e0b..52121ebdda4 100644 --- a/sysid/src/main/native/cpp/analysis/AnalysisManager.cpp +++ b/sysid/src/main/native/cpp/analysis/AnalysisManager.cpp @@ -5,24 +5,39 @@ #include "sysid/analysis/AnalysisManager.h" #include -#include #include -#include #include #include -#include +#include #include #include #include -#include "sysid/Util.h" #include "sysid/analysis/FilteringUtils.h" -#include "sysid/analysis/JSONConverter.h" -#include "sysid/analysis/TrackWidthAnalysis.h" using namespace sysid; +static double Lerp(units::second_t time, + std::vector>& data) { + auto next = std::find_if(data.begin(), data.end(), [&](const auto& entry) { + return entry.time > time; + }); + + if (next == data.begin()) { + next++; + } + + if (next == data.end()) { + next--; + } + + const auto prev = next - 1; + + return wpi::Lerp(prev->measurement, next->measurement, + (time - prev->time) / (next->time - prev->time)); +} + /** * Converts a raw data vector into a PreparedData vector with only the * timestamp, voltage, position, and velocity fields filled out. @@ -38,18 +53,25 @@ using namespace sysid; * * @return A PreparedData vector */ -template -static std::vector ConvertToPrepared( - const std::vector>& data) { +static std::vector ConvertToPrepared(const MotorData& data) { std::vector prepared; - for (int i = 0; i < static_cast(data.size()) - 1; ++i) { - const auto& pt1 = data[i]; - const auto& pt2 = data[i + 1]; - prepared.emplace_back(PreparedData{ - units::second_t{pt1[Timestamp]}, pt1[Voltage], pt1[Position], - pt1[Velocity], units::second_t{pt2[Timestamp] - pt1[Timestamp]}}); + // assume we've selected down to a single contiguous run by this point + auto run = data.runs[0]; + + for (int i = 0; i < static_cast(run.voltage.size()) - 1; ++i) { + const auto& currentVoltage = run.voltage[i]; + const auto& nextVoltage = run.voltage[i + 1]; + + auto currentPosition = Lerp(currentVoltage.time, run.position); + + auto currentVelocity = Lerp(currentVoltage.time, run.velocity); + + prepared.emplace_back(PreparedData{currentVoltage.time, + currentVoltage.measurement.value(), + currentPosition, currentVelocity, + nextVoltage.time - currentVoltage.time}); } + return prepared; } @@ -62,18 +84,16 @@ static std::vector ConvertToPrepared( * * @param dataset A reference to the dataset being used */ -template -static void CopyRawData( - wpi::StringMap>>* dataset) { +static void CopyRawData(wpi::StringMap* dataset) { auto& data = *dataset; // Loads the Raw Data for (auto& it : data) { auto key = it.first(); - auto& dataset = it.getValue(); + auto& motorData = it.getValue(); if (!wpi::contains(key, "raw")) { - data[fmt::format("raw-{}", key)] = dataset; - data[fmt::format("original-raw-{}", key)] = dataset; + data[fmt::format("raw-{}", key)] = motorData; + data[fmt::format("original-raw-{}", key)] = motorData; } } } @@ -94,416 +114,73 @@ static Storage CombineDatasets(const std::vector& slowForward, } void AnalysisManager::PrepareGeneralData() { - using Data = std::array; - wpi::StringMap> data; wpi::StringMap> preparedData; - // Store the raw data columns. - static constexpr size_t kTimeCol = 0; - static constexpr size_t kVoltageCol = 1; - static constexpr size_t kPosCol = 2; - static constexpr size_t kVelCol = 3; - - WPI_INFO(m_logger, "{}", "Reading JSON data."); - // Get the major components from the JSON and store them inside a StringMap. - for (auto&& key : AnalysisManager::kJsonDataKeys) { - data[key] = m_json.at(key).get>(); - } - WPI_INFO(m_logger, "{}", "Preprocessing raw data."); - // Ensure that voltage and velocity have the same sign. Also multiply - // positions and velocities by the factor. - for (auto it = data.begin(); it != data.end(); ++it) { - for (auto&& pt : it->second) { - pt[kVoltageCol] = std::copysign(pt[kVoltageCol], pt[kVelCol]); - pt[kPosCol] *= m_factor; - pt[kVelCol] *= m_factor; - } - } WPI_INFO(m_logger, "{}", "Copying raw data."); - CopyRawData(&data); + CopyRawData(&m_data.motorData); WPI_INFO(m_logger, "{}", "Converting raw data to PreparedData struct."); // Convert data to PreparedData structs - for (auto& it : data) { + for (auto& it : m_data.motorData) { auto key = it.first(); - preparedData[key] = - ConvertToPrepared<4, kTimeCol, kVoltageCol, kPosCol, kVelCol>( - data[key]); + preparedData[key] = ConvertToPrepared(m_data.motorData[key]); + WPI_INFO(m_logger, "SAMPLES {}", preparedData[key].size()); } // Store the original datasets - m_originalDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kCombined)] = - CombineDatasets(preparedData["original-raw-slow-forward"], - preparedData["original-raw-slow-backward"], - preparedData["original-raw-fast-forward"], - preparedData["original-raw-fast-backward"]); + m_originalDataset = + CombineDatasets(preparedData["original-raw-quasistatic-forward"], + preparedData["original-raw-quasistatic-reverse"], + preparedData["original-raw-dynamic-forward"], + preparedData["original-raw-dynamic-reverse"]); WPI_INFO(m_logger, "{}", "Initial trimming and filtering."); sysid::InitialTrimAndFilter(&preparedData, &m_settings, m_positionDelays, m_velocityDelays, m_minStepTime, m_maxStepTime, - m_unit); + m_data.distanceUnit); + + WPI_INFO(m_logger, "{}", m_minStepTime); + WPI_INFO(m_logger, "{}", m_maxStepTime); WPI_INFO(m_logger, "{}", "Acceleration filtering."); sysid::AccelFilter(&preparedData); WPI_INFO(m_logger, "{}", "Storing datasets."); // Store the raw datasets - m_rawDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kCombined)] = - CombineDatasets( - preparedData["raw-slow-forward"], preparedData["raw-slow-backward"], - preparedData["raw-fast-forward"], preparedData["raw-fast-backward"]); + m_rawDataset = CombineDatasets(preparedData["raw-quasistatic-forward"], + preparedData["raw-quasistatic-reverse"], + preparedData["raw-dynamic-forward"], + preparedData["raw-dynamic-reverse"]); // Store the filtered datasets - m_filteredDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kCombined)] = - CombineDatasets( - preparedData["slow-forward"], preparedData["slow-backward"], - preparedData["fast-forward"], preparedData["fast-backward"]); - - m_startTimes = {preparedData["raw-slow-forward"][0].timestamp, - preparedData["raw-slow-backward"][0].timestamp, - preparedData["raw-fast-forward"][0].timestamp, - preparedData["raw-fast-backward"][0].timestamp}; -} - -void AnalysisManager::PrepareAngularDrivetrainData() { - using Data = std::array; - wpi::StringMap> data; - wpi::StringMap> preparedData; - - // Store the relevant raw data columns. - static constexpr size_t kTimeCol = 0; - static constexpr size_t kLVoltageCol = 1; - static constexpr size_t kRVoltageCol = 2; - static constexpr size_t kLPosCol = 3; - static constexpr size_t kRPosCol = 4; - static constexpr size_t kLVelCol = 5; - static constexpr size_t kRVelCol = 6; - static constexpr size_t kAngleCol = 7; - static constexpr size_t kAngularRateCol = 8; - - WPI_INFO(m_logger, "{}", "Reading JSON data."); - // Get the major components from the JSON and store them inside a StringMap. - for (auto&& key : AnalysisManager::kJsonDataKeys) { - data[key] = m_json.at(key).get>(); - } - - WPI_INFO(m_logger, "{}", "Preprocessing raw data."); - // Ensure that voltage and velocity have the same sign. Also multiply - // positions and velocities by the factor. - for (auto it = data.begin(); it != data.end(); ++it) { - for (auto&& pt : it->second) { - pt[kLPosCol] *= m_factor; - pt[kRPosCol] *= m_factor; - pt[kLVelCol] *= m_factor; - pt[kRVelCol] *= m_factor; - - // Stores the average voltages in the left voltage column. - // This aggregates the left and right voltages into a single voltage - // column for the ConvertToPrepared() method. std::copysign() ensures the - // polarity of the voltage matches the direction the robot turns. - pt[kLVoltageCol] = std::copysign( - (std::abs(pt[kLVoltageCol]) + std::abs(pt[kRVoltageCol])) / 2, - pt[kAngularRateCol]); - - // ω = (v_r - v_l) / trackwidth - // v = ωr => v = ω * trackwidth / 2 - // (v_r - v_l) / trackwidth * (trackwidth / 2) = (v_r - v_l) / 2 - // However, since we know this is an angular test, the left and right - // wheel velocities will have opposite signs, allowing us to add their - // absolute values and get the same result (in terms of magnitude). - // std::copysign() is used to make sure the direction of the wheel - // velocities matches the direction the robot turns. - pt[kAngularRateCol] = - std::copysign((std::abs(pt[kRVelCol]) + std::abs(pt[kLVelCol])) / 2, - pt[kAngularRateCol]); - } - } - - WPI_INFO(m_logger, "{}", "Calculating trackwidth"); - // Aggregating all the deltas from all the tests - double leftDelta = 0.0; - double rightDelta = 0.0; - double angleDelta = 0.0; - for (const auto& it : data) { - auto key = it.first(); - auto& trackWidthData = data[key]; - leftDelta += std::abs(trackWidthData.back()[kLPosCol] - - trackWidthData.front()[kLPosCol]); - rightDelta += std::abs(trackWidthData.back()[kRPosCol] - - trackWidthData.front()[kRPosCol]); - angleDelta += std::abs(trackWidthData.back()[kAngleCol] - - trackWidthData.front()[kAngleCol]); - } - m_trackWidth = sysid::CalculateTrackWidth(leftDelta, rightDelta, - units::radian_t{angleDelta}); - - WPI_INFO(m_logger, "{}", "Copying raw data."); - CopyRawData(&data); - - WPI_INFO(m_logger, "{}", "Converting to PreparedData struct."); - // Convert raw data to prepared data - for (const auto& it : data) { - auto key = it.first(); - preparedData[key] = ConvertToPrepared<9, kTimeCol, kLVoltageCol, kAngleCol, - kAngularRateCol>(data[key]); - } - - // Create the distinct datasets and store them - m_originalDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kCombined)] = - CombineDatasets(preparedData["original-raw-slow-forward"], - preparedData["original-raw-slow-backward"], - preparedData["original-raw-fast-forward"], - preparedData["original-raw-fast-backward"]); - - WPI_INFO(m_logger, "{}", "Applying trimming and filtering."); - sysid::InitialTrimAndFilter(&preparedData, &m_settings, m_positionDelays, - m_velocityDelays, m_minStepTime, m_maxStepTime); - - WPI_INFO(m_logger, "{}", "Acceleration filtering."); - sysid::AccelFilter(&preparedData); - - WPI_INFO(m_logger, "{}", "Storing datasets."); - // Create the distinct datasets and store them - m_rawDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kCombined)] = - CombineDatasets( - preparedData["raw-slow-forward"], preparedData["raw-slow-backward"], - preparedData["raw-fast-forward"], preparedData["raw-fast-backward"]); - m_filteredDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kCombined)] = - CombineDatasets( - preparedData["slow-forward"], preparedData["slow-backward"], - preparedData["fast-forward"], preparedData["fast-backward"]); - - m_startTimes = {preparedData["slow-forward"][0].timestamp, - preparedData["slow-backward"][0].timestamp, - preparedData["fast-forward"][0].timestamp, - preparedData["fast-backward"][0].timestamp}; -} - -void AnalysisManager::PrepareLinearDrivetrainData() { - using Data = std::array; - wpi::StringMap> data; - wpi::StringMap> preparedData; - - // Store the relevant raw data columns. - static constexpr size_t kTimeCol = 0; - static constexpr size_t kLVoltageCol = 1; - static constexpr size_t kRVoltageCol = 2; - static constexpr size_t kLPosCol = 3; - static constexpr size_t kRPosCol = 4; - static constexpr size_t kLVelCol = 5; - static constexpr size_t kRVelCol = 6; - - // Get the major components from the JSON and store them inside a StringMap. - WPI_INFO(m_logger, "{}", "Reading JSON data."); - for (auto&& key : AnalysisManager::kJsonDataKeys) { - data[key] = m_json.at(key).get>(); - } - - // Ensure that voltage and velocity have the same sign. Also multiply - // positions and velocities by the factor. - WPI_INFO(m_logger, "{}", "Preprocessing raw data."); - for (auto it = data.begin(); it != data.end(); ++it) { - for (auto&& pt : it->second) { - pt[kLVoltageCol] = std::copysign(pt[kLVoltageCol], pt[kLVelCol]); - pt[kRVoltageCol] = std::copysign(pt[kRVoltageCol], pt[kRVelCol]); - pt[kLPosCol] *= m_factor; - pt[kRPosCol] *= m_factor; - pt[kLVelCol] *= m_factor; - pt[kRVelCol] *= m_factor; - } - } - - WPI_INFO(m_logger, "{}", "Copying raw data."); - CopyRawData(&data); - - // Convert data to PreparedData - WPI_INFO(m_logger, "{}", "Converting to PreparedData struct."); - for (auto& it : data) { - auto key = it.first(); - - preparedData[fmt::format("left-{}", key)] = - ConvertToPrepared<9, kTimeCol, kLVoltageCol, kLPosCol, kLVelCol>( - data[key]); - preparedData[fmt::format("right-{}", key)] = - ConvertToPrepared<9, kTimeCol, kRVoltageCol, kRPosCol, kRVelCol>( - data[key]); - } - - // Create the distinct raw datasets and store them - auto originalSlowForward = AnalysisManager::DataConcat( - preparedData["left-original-raw-slow-forward"], - preparedData["right-original-raw-slow-forward"]); - auto originalSlowBackward = AnalysisManager::DataConcat( - preparedData["left-original-raw-slow-backward"], - preparedData["right-original-raw-slow-backward"]); - auto originalFastForward = AnalysisManager::DataConcat( - preparedData["left-original-raw-fast-forward"], - preparedData["right-original-raw-fast-forward"]); - auto originalFastBackward = AnalysisManager::DataConcat( - preparedData["left-original-raw-fast-backward"], - preparedData["right-original-raw-fast-backward"]); - m_originalDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kCombined)] = - CombineDatasets(originalSlowForward, originalSlowBackward, - originalFastForward, originalFastBackward); - m_originalDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kLeft)] = - CombineDatasets(preparedData["left-original-raw-slow-forward"], - preparedData["left-original-raw-slow-backward"], - preparedData["left-original-raw-fast-forward"], - preparedData["left-original-raw-fast-backward"]); - m_originalDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kRight)] = - CombineDatasets(preparedData["right-original-raw-slow-forward"], - preparedData["right-original-raw-slow-backward"], - preparedData["right-original-raw-fast-forward"], - preparedData["right-original-raw-fast-backward"]); - - WPI_INFO(m_logger, "{}", "Applying trimming and filtering."); - sysid::InitialTrimAndFilter(&preparedData, &m_settings, m_positionDelays, - m_velocityDelays, m_minStepTime, m_maxStepTime); - - auto slowForward = AnalysisManager::DataConcat( - preparedData["left-slow-forward"], preparedData["right-slow-forward"]); - auto slowBackward = AnalysisManager::DataConcat( - preparedData["left-slow-backward"], preparedData["right-slow-backward"]); - auto fastForward = AnalysisManager::DataConcat( - preparedData["left-fast-forward"], preparedData["right-fast-forward"]); - auto fastBackward = AnalysisManager::DataConcat( - preparedData["left-fast-backward"], preparedData["right-fast-backward"]); - - WPI_INFO(m_logger, "{}", "Acceleration filtering."); - sysid::AccelFilter(&preparedData); - - WPI_INFO(m_logger, "{}", "Storing datasets."); - - // Create the distinct raw datasets and store them - auto rawSlowForward = - AnalysisManager::DataConcat(preparedData["left-raw-slow-forward"], - preparedData["right-raw-slow-forward"]); - auto rawSlowBackward = - AnalysisManager::DataConcat(preparedData["left-raw-slow-backward"], - preparedData["right-raw-slow-backward"]); - auto rawFastForward = - AnalysisManager::DataConcat(preparedData["left-raw-fast-forward"], - preparedData["right-raw-fast-forward"]); - auto rawFastBackward = - AnalysisManager::DataConcat(preparedData["left-raw-fast-backward"], - preparedData["right-raw-fast-backward"]); - - m_rawDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kCombined)] = - CombineDatasets(rawSlowForward, rawSlowBackward, rawFastForward, - rawFastBackward); - m_rawDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kLeft)] = - CombineDatasets(preparedData["left-raw-slow-forward"], - preparedData["left-raw-slow-backward"], - preparedData["left-raw-fast-forward"], - preparedData["left-raw-fast-backward"]); - m_rawDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kRight)] = - CombineDatasets(preparedData["right-raw-slow-forward"], - preparedData["right-raw-slow-backward"], - preparedData["right-raw-fast-forward"], - preparedData["right-raw-fast-backward"]); - - // Create the distinct filtered datasets and store them - m_filteredDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kCombined)] = - CombineDatasets(slowForward, slowBackward, fastForward, fastBackward); - m_filteredDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kLeft)] = - CombineDatasets(preparedData["left-slow-forward"], - preparedData["left-slow-backward"], - preparedData["left-fast-forward"], - preparedData["left-fast-backward"]); - m_filteredDataset[static_cast( - AnalysisManager::Settings::DrivetrainDataset::kRight)] = - CombineDatasets(preparedData["right-slow-forward"], - preparedData["right-slow-backward"], - preparedData["right-fast-forward"], - preparedData["right-fast-backward"]); - - m_startTimes = { - rawSlowForward.front().timestamp, rawSlowBackward.front().timestamp, - rawFastForward.front().timestamp, rawFastBackward.front().timestamp}; + m_filteredDataset = CombineDatasets( + preparedData["quasistatic-forward"], preparedData["quasistatic-reverse"], + preparedData["dynamic-forward"], preparedData["dynamic-reverse"]); + + m_startTimes = {preparedData["raw-quasistatic-forward"][0].timestamp, + preparedData["raw-quasistatic-reverse"][0].timestamp, + preparedData["raw-dynamic-forward"][0].timestamp, + preparedData["raw-dynamic-reverse"][0].timestamp}; } AnalysisManager::AnalysisManager(Settings& settings, wpi::Logger& logger) - : m_logger{logger}, - m_settings{settings}, - m_type{analysis::kSimple}, - m_unit{"Meters"}, - m_factor{1} {} + : m_logger{logger}, m_settings{settings} {} -AnalysisManager::AnalysisManager(std::string_view path, Settings& settings, +AnalysisManager::AnalysisManager(TestData data, Settings& settings, wpi::Logger& logger) - : m_logger{logger}, m_settings{settings} { - { - // Read JSON from the specified path - std::error_code ec; - std::unique_ptr fileBuffer = - wpi::MemoryBuffer::GetFile(path, ec); - if (fileBuffer == nullptr || ec) { - throw FileReadingError(path); - } - - m_json = wpi::json::parse(fileBuffer->GetCharBuffer()); - - WPI_INFO(m_logger, "Read {}", path); - } - - // Check that we have a sysid JSON - if (m_json.find("sysid") == m_json.end()) { - // If it's not a sysid JSON, try converting it from frc-char format - std::string newPath = sysid::ConvertJSON(path, logger); - - // Read JSON from the specified path - std::error_code ec; - std::unique_ptr fileBuffer = - wpi::MemoryBuffer::GetFile(path, ec); - if (fileBuffer == nullptr || ec) { - throw FileReadingError(newPath); - } - - m_json = wpi::json::parse(fileBuffer->GetCharBuffer()); - - WPI_INFO(m_logger, "Read {}", newPath); - } - - WPI_INFO(m_logger, "Parsing initial data of {}", path); - // Get the analysis type from the JSON. - m_type = sysid::analysis::FromName(m_json.at("test").get()); - - // Get the rotation -> output units factor from the JSON. - m_unit = m_json.at("units").get(); - m_factor = m_json.at("unitsPerRotation").get(); - WPI_DEBUG(m_logger, "Parsing units per rotation as {} {} per rotation", - m_factor, m_unit); - + : m_data{std::move(data)}, m_logger{logger}, m_settings{settings} { // Reset settings for Dynamic Test Limits m_settings.stepTestDuration = units::second_t{0.0}; - m_settings.motionThreshold = std::numeric_limits::infinity(); + m_settings.velocityThreshold = std::numeric_limits::infinity(); } void AnalysisManager::PrepareData() { - WPI_INFO(m_logger, "Preparing {} data", m_type.name); - if (m_type == analysis::kDrivetrain) { - PrepareLinearDrivetrainData(); - } else if (m_type == analysis::kDrivetrainAngular) { - PrepareAngularDrivetrainData(); - } else { - PrepareGeneralData(); - } + // WPI_INFO(m_logger, "Preparing {} data", m_data.mechanismType.name); + + PrepareGeneralData(); + WPI_INFO(m_logger, "{}", "Finished Preparing Data"); } @@ -515,54 +192,97 @@ AnalysisManager::FeedforwardGains AnalysisManager::CalculateFeedforward() { WPI_INFO(m_logger, "{}", "Calculating Gains"); // Calculate feedforward gains from the data. - const auto& ff = sysid::CalculateFeedforwardGains(GetFilteredData(), m_type); - FeedforwardGains ffGains = {ff, m_trackWidth}; + const auto& analysisType = m_data.mechanismType; + const auto& ff = + sysid::CalculateFeedforwardGains(GetFilteredData(), analysisType, false); const auto& Ks = ff.coeffs[0]; + FeedforwardGain KsGain = { + .gain = Ks, .descriptor = "Voltage needed to overcome static friction."}; + if (Ks < 0) { + KsGain.isValidGain = false; + KsGain.errorMessage = fmt::format( + "Calculated Ks gain of: {0:.3f} is erroneous! Ks should be >= 0.", Ks); + } + const auto& Kv = ff.coeffs[1]; + FeedforwardGain KvGain = { + .gain = Kv, + .descriptor = + "Voltage needed to hold/cruise at a constant velocity while " + "overcoming the counter-electromotive force and any additional " + "friction."}; + if (Kv < 0) { + KvGain.isValidGain = false; + KvGain.errorMessage = fmt::format( + "Calculated Kv gain of: {0:.3f} is erroneous! Kv should be >= 0.", Kv); + } + const auto& Ka = ff.coeffs[2]; + FeedforwardGain KaGain = { + .gain = Ka, + .descriptor = + "Voltage needed to induce a given acceleration in the motor shaft."}; + if (Ka <= 0) { + KaGain.isValidGain = false; + KaGain.errorMessage = fmt::format( + "Calculated Ka gain of: {0:.3f} is erroneous! Ka should be > 0.", Ka); + } - if (Ka <= 0 || Kv < 0) { - throw InvalidDataError( - fmt::format("The calculated feedforward gains of kS: {}, Kv: {}, Ka: " - "{} are erroneous. Your Ka should be > 0 while your Kv and " - "Ks constants should both >= 0. Try adjusting the " - "filtering and trimming settings or collect better data.", - Ks, Kv, Ka)); + if (analysisType == analysis::kSimple) { + return FeedforwardGains{ + .olsResult = ff, .Ks = KsGain, .Kv = KvGain, .Ka = KaGain}; } - return ffGains; + if (analysisType == analysis::kElevator || analysisType == analysis::kArm) { + const auto& Kg = ff.coeffs[3]; + FeedforwardGain KgGain = { + Kg, "Voltage needed to counteract the force of gravity."}; + if (Kg < 0) { + KgGain.isValidGain = false; + KgGain.errorMessage = fmt::format( + "Calculated Kg gain of: {0:.3f} is erroneous! Kg should be >= 0.", + Ka); + } + + // Elevator analysis only requires Kg + if (analysisType == analysis::kElevator) { + return FeedforwardGains{.olsResult = ff, + .Ks = KsGain, + .Kv = KvGain, + .Ka = KaGain, + .Kg = KgGain}; + } else { + // Arm analysis requires Kg and an angle offset + FeedforwardGain offset = { + .gain = ff.coeffs[4], + .descriptor = + "This is the angle offset which, when added to the angle " + "measurement, zeroes it out when the arm is horizontal. This is " + "needed for the arm feedforward to work."}; + return FeedforwardGains{ff, KsGain, KvGain, KaGain, KgGain, offset}; + } + } + + return FeedforwardGains{.olsResult = ff}; } sysid::FeedbackGains AnalysisManager::CalculateFeedback( - std::vector ff) { - const auto& Kv = ff[1]; - const auto& Ka = ff[2]; + const FeedforwardGain& Kv, const FeedforwardGain& Ka) { FeedbackGains fb; if (m_settings.type == FeedbackControllerLoopType::kPosition) { fb = sysid::CalculatePositionFeedbackGains( - m_settings.preset, m_settings.lqr, Kv, Ka, - m_settings.convertGainsToEncTicks - ? m_settings.gearing * m_settings.cpr * m_factor - : 1); + m_settings.preset, m_settings.lqr, Kv.gain, Ka.gain); } else { fb = sysid::CalculateVelocityFeedbackGains( - m_settings.preset, m_settings.lqr, Kv, Ka, - m_settings.convertGainsToEncTicks - ? m_settings.gearing * m_settings.cpr * m_factor - : 1); + m_settings.preset, m_settings.lqr, Kv.gain, Ka.gain); } return fb; } -void AnalysisManager::OverrideUnits(std::string_view unit, - double unitsPerRotation) { - m_unit = unit; - m_factor = unitsPerRotation; +void AnalysisManager::OverrideUnits(std::string_view unit) { + m_data.distanceUnit = unit; } -void AnalysisManager::ResetUnitsFromJSON() { - m_unit = m_json.at("units").get(); - m_factor = m_json.at("unitsPerRotation").get(); -} +void AnalysisManager::ResetUnitsFromJSON() {} diff --git a/sysid/src/main/native/cpp/analysis/AnalysisType.cpp b/sysid/src/main/native/cpp/analysis/AnalysisType.cpp index 18b461fe6d8..6ef27c93875 100644 --- a/sysid/src/main/native/cpp/analysis/AnalysisType.cpp +++ b/sysid/src/main/native/cpp/analysis/AnalysisType.cpp @@ -7,12 +7,6 @@ using namespace sysid; AnalysisType sysid::analysis::FromName(std::string_view name) { - if (name == "Drivetrain") { - return sysid::analysis::kDrivetrain; - } - if (name == "Drivetrain (Angular)") { - return sysid::analysis::kDrivetrainAngular; - } if (name == "Elevator") { return sysid::analysis::kElevator; } diff --git a/sysid/src/main/native/cpp/analysis/FeedbackAnalysis.cpp b/sysid/src/main/native/cpp/analysis/FeedbackAnalysis.cpp index 0691aaf6ebb..7f8a3765e3f 100644 --- a/sysid/src/main/native/cpp/analysis/FeedbackAnalysis.cpp +++ b/sysid/src/main/native/cpp/analysis/FeedbackAnalysis.cpp @@ -20,7 +20,7 @@ using Ka_t = decltype(1_V / 1_mps_sq); FeedbackGains sysid::CalculatePositionFeedbackGains( const FeedbackControllerPreset& preset, const LQRParameters& params, - double Kv, double Ka, double encFactor) { + double Kv, double Ka) { // If acceleration requires no effort, velocity becomes an input for position // control. We choose an appropriate model in this case to avoid numerical // instabilities in the LQR. @@ -34,9 +34,9 @@ FeedbackGains sysid::CalculatePositionFeedbackGains( // Compensate for any latency from sensor measurements, filtering, etc. controller.LatencyCompensate(system, preset.period, 0.0_s); - return {controller.K(0, 0) * preset.outputConversionFactor / encFactor, + return {controller.K(0, 0) * preset.outputConversionFactor, controller.K(0, 1) * preset.outputConversionFactor / - (encFactor * (preset.normalized ? 1 : preset.period.value()))}; + (preset.normalized ? 1 : preset.period.value())}; } // This is our special model to avoid instabilities in the LQR. @@ -49,8 +49,7 @@ FeedbackGains sysid::CalculatePositionFeedbackGains( // Compensate for any latency from sensor measurements, filtering, etc. controller.LatencyCompensate(system, preset.period, 0.0_s); - return {Kv * controller.K(0, 0) * preset.outputConversionFactor / encFactor, - 0.0}; + return {Kv * controller.K(0, 0) * preset.outputConversionFactor, 0.0}; } FeedbackGains sysid::CalculateVelocityFeedbackGains( diff --git a/sysid/src/main/native/cpp/analysis/FeedforwardAnalysis.cpp b/sysid/src/main/native/cpp/analysis/FeedforwardAnalysis.cpp index a2c0f2d3342..110ff9463f1 100644 --- a/sysid/src/main/native/cpp/analysis/FeedforwardAnalysis.cpp +++ b/sysid/src/main/native/cpp/analysis/FeedforwardAnalysis.cpp @@ -92,38 +92,51 @@ static void CheckOLSDataQuality(const Eigen::MatrixXd& X, constexpr double threshold = 10.0; - // For n x n matrix XᵀX, need n - 1 nonzero eigenvalues for good fit + // For n x n matrix XᵀX, need n nonzero eigenvalues for good fit for (int row = 0; row < eigvals.rows(); ++row) { - if (std::abs(eigvals(row)) <= threshold) { - // Find row of eigenvector with largest magnitude. This determines which - // gain is rank-deficient - int maxIndex; - eigvecs.col(row).cwiseAbs().maxCoeff(&maxIndex); - - // Fit for α is rank-deficient + // Find row of eigenvector with largest magnitude. This determines the + // primary regression variable that corresponds to the eigenvalue. + int maxIndex; + double maxCoeff = eigvecs.col(row).cwiseAbs().maxCoeff(&maxIndex); + + // Check whether the eigenvector component along the regression variable's + // direction is below the threshold. If it is, the regression variable's fit + // is bad. + if (std::abs(eigvals(row) * maxCoeff) <= threshold) { + // Fit for α is bad if (maxIndex == 0) { + // Affects Kv badGains.set(1); } - // Fit for β is rank-deficient + + // Fit for β is bad if (maxIndex == 1) { + // Affects all gains badGains.set(); break; } - // Fit for γ is rank-deficient + + // Fit for γ is bad if (maxIndex == 2) { + // Affects Ks badGains.set(0); } - // Fit for δ is rank-deficient + + // Fit for δ is bad if (maxIndex == 3) { if (type == analysis::kElevator) { + // Affects Kg badGains.set(3); } else if (type == analysis::kArm) { + // Affects Kg and offset badGains.set(3); badGains.set(4); } } - // Fit for ε is rank-deficient + + // Fit for ε is bad if (maxIndex == 4) { + // Affects Kg and offset badGains.set(3); badGains.set(4); } diff --git a/sysid/src/main/native/cpp/analysis/FilteringUtils.cpp b/sysid/src/main/native/cpp/analysis/FilteringUtils.cpp index 6c66ef8b424..db0381103aa 100644 --- a/sysid/src/main/native/cpp/analysis/FilteringUtils.cpp +++ b/sysid/src/main/native/cpp/analysis/FilteringUtils.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include using namespace sysid; @@ -126,7 +127,7 @@ sysid::TrimStepVoltageData(std::vector* data, auto motionBegins = std::find_if( data->begin(), data->end(), [settings, firstPosition](auto& datum) { return std::abs(datum.position - firstPosition) > - (settings->motionThreshold * datum.dt.value()); + (settings->velocityThreshold * datum.dt.value()); }); units::second_t positionDelay; @@ -138,7 +139,11 @@ sysid::TrimStepVoltageData(std::vector* data, auto maxAccel = std::max_element( data->begin(), data->end(), [](const auto& a, const auto& b) { - return std::abs(a.acceleration) < std::abs(b.acceleration); + // Since we don't know if its a forward or backwards test here, we use + // the sign of each point's velocity to determine how to compare their + // accelerations. + return wpi::sgn(a.velocity) * a.acceleration < + wpi::sgn(b.velocity) * b.acceleration; }); units::second_t velocityDelay; @@ -153,32 +158,28 @@ sysid::TrimStepVoltageData(std::vector* data, minStepTime = std::min(data->at(0).timestamp - firstTimestamp, minStepTime); - // If step duration hasn't been set yet, calculate a default (find the entry - // before the acceleration first hits zero) - if (settings->stepTestDuration <= minStepTime) { - // Get noise floor - const double accelNoiseFloor = GetNoiseFloor( - *data, kNoiseMeanWindow, [](auto&& pt) { return pt.acceleration; }); - // Find latest element with nonzero acceleration + // If step test duration not yet specified, calculate default + if (settings->stepTestDuration == 0_s) { + // Find maximum speed reached + const auto maxSpeed = + GetMaxSpeed(*data, [](auto&& pt) { return pt.velocity; }); + // Find place where 90% of maximum speed exceeded auto endIt = std::find_if( - data->rbegin(), data->rend(), [&](const PreparedData& entry) { - return std::abs(entry.acceleration) > accelNoiseFloor; + data->begin(), data->end(), [&](const PreparedData& entry) { + return std::abs(entry.velocity) > maxSpeed * 0.9; }); - if (endIt != data->rend()) { - // Calculate default duration - settings->stepTestDuration = std::min( - endIt->timestamp - data->front().timestamp + minStepTime + 1_s, - maxStepTime); - } else { - settings->stepTestDuration = maxStepTime; + if (endIt != data->end()) { + settings->stepTestDuration = + std::min(endIt->timestamp - data->front().timestamp + minStepTime, + maxStepTime); } } // Find first entry greater than the step test duration auto maxIt = std::find_if(data->begin(), data->end(), [&](PreparedData entry) { - return entry.timestamp - data->front().timestamp + minStepTime > + return entry.timestamp - data->front().timestamp > settings->stepTestDuration; }); @@ -186,6 +187,7 @@ sysid::TrimStepVoltageData(std::vector* data, if (maxIt != data->end()) { data->erase(maxIt, data->end()); } + return std::make_tuple(minStepTime, positionDelay, velocityDelay); } @@ -204,6 +206,16 @@ double sysid::GetNoiseFloor( return std::sqrt(sum / (data.size() - step)); } +double sysid::GetMaxSpeed( + const std::vector& data, + std::function accessorFunction) { + double max = 0.0; + for (size_t i = 0; i < data.size(); i++) { + max = std::max(max, std::abs(accessorFunction(data[i]))); + } + return max; +} + units::second_t sysid::GetMeanTimeDelta(const std::vector& data) { std::vector dts; @@ -301,7 +313,7 @@ static units::second_t GetMaxStepTime( auto key = it.first(); auto& dataset = it.getValue(); - if (IsRaw(key) && wpi::contains(key, "fast")) { + if (IsRaw(key) && wpi::contains(key, "dynamic")) { auto duration = dataset.back().timestamp - dataset.front().timestamp; if (duration > maxStepTime) { maxStepTime = duration; @@ -323,13 +335,13 @@ void sysid::InitialTrimAndFilter( maxStepTime = GetMaxStepTime(preparedData); // Calculate Velocity Threshold if it hasn't been set yet - if (settings->motionThreshold == std::numeric_limits::infinity()) { + if (settings->velocityThreshold == std::numeric_limits::infinity()) { for (auto& it : preparedData) { auto key = it.first(); auto& dataset = it.getValue(); - if (wpi::contains(key, "slow")) { - settings->motionThreshold = - std::min(settings->motionThreshold, + if (wpi::contains(key, "quasistatic")) { + settings->velocityThreshold = + std::min(settings->velocityThreshold, GetNoiseFloor(dataset, kNoiseMeanWindow, [](auto&& pt) { return pt.velocity; })); } @@ -341,13 +353,13 @@ void sysid::InitialTrimAndFilter( auto& dataset = it.getValue(); // Trim quasistatic test data to remove all points where voltage is zero or - // velocity < motion threshold. - if (wpi::contains(key, "slow")) { + // velocity < velocity threshold. + if (wpi::contains(key, "quasistatic")) { dataset.erase(std::remove_if(dataset.begin(), dataset.end(), [&](const auto& pt) { return std::abs(pt.voltage) <= 0 || std::abs(pt.velocity) < - settings->motionThreshold; + settings->velocityThreshold; }), dataset.end()); @@ -366,7 +378,7 @@ void sysid::InitialTrimAndFilter( PrepareMechData(&dataset, unit); // Trims filtered Dynamic Test Data - if (IsFiltered(key) && wpi::contains(key, "fast")) { + if (IsFiltered(key) && wpi::contains(key, "dynamic")) { // Get the filtered dataset name auto filteredKey = RemoveStr(key, "raw-"); diff --git a/sysid/src/main/native/cpp/analysis/JSONConverter.cpp b/sysid/src/main/native/cpp/analysis/JSONConverter.cpp deleted file mode 100644 index 5060679d989..00000000000 --- a/sysid/src/main/native/cpp/analysis/JSONConverter.cpp +++ /dev/null @@ -1,164 +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 "sysid/analysis/JSONConverter.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "sysid/Util.h" -#include "sysid/analysis/AnalysisManager.h" -#include "sysid/analysis/AnalysisType.h" - -// Sizes of the arrays for new sysid data. -static constexpr size_t kDrivetrainSize = 9; -static constexpr size_t kGeneralSize = 4; - -// Indices for the old data. -static constexpr size_t kTimestampCol = 0; -static constexpr size_t kLVoltsCol = 3; -static constexpr size_t kRVoltsCol = 4; -static constexpr size_t kLPosCol = 5; -static constexpr size_t kRPosCol = 6; -static constexpr size_t kLVelCol = 7; -static constexpr size_t kRVelCol = 8; - -static wpi::json GetJSON(std::string_view path, wpi::Logger& logger) { - std::error_code ec; - std::unique_ptr fileBuffer = - wpi::MemoryBuffer::GetFile(path, ec); - if (fileBuffer == nullptr || ec) { - throw std::runtime_error(fmt::format("Unable to read: {}", path)); - } - - wpi::json json = wpi::json::parse(fileBuffer->GetCharBuffer()); - WPI_INFO(logger, "Read frc-characterization JSON from {}", path); - return json; -} - -std::string sysid::ConvertJSON(std::string_view path, wpi::Logger& logger) { - wpi::json ojson = GetJSON(path, logger); - - auto type = sysid::analysis::FromName(ojson.at("test").get()); - auto factor = ojson.at("unitsPerRotation").get(); - auto unit = ojson.at("units").get(); - - wpi::json json; - for (auto&& key : AnalysisManager::kJsonDataKeys) { - if (type == analysis::kDrivetrain) { - // Get the old data; create a vector for the new data; reserve the - // appropriate size for the new data. - auto odata = ojson.at(key).get>>(); - std::vector> data; - data.reserve(odata.size()); - - // Transfer the data. - for (auto&& pt : odata) { - data.push_back(std::array{ - pt[kTimestampCol], pt[kLVoltsCol], pt[kRVoltsCol], pt[kLPosCol], - pt[kRPosCol], pt[kLVelCol], pt[kRVelCol], 0.0, 0.0}); - } - json[key] = data; - } else { - // Get the old data; create a vector for the new data; reserve the - // appropriate size for the new data. - auto odata = ojson.at(key).get>>(); - std::vector> data; - data.reserve(odata.size()); - - // Transfer the data. - for (auto&& pt : odata) { - data.push_back(std::array{ - pt[kTimestampCol], pt[kLVoltsCol], pt[kLPosCol], pt[kLVelCol]}); - } - json[key] = data; - } - } - json["units"] = unit; - json["unitsPerRotation"] = factor; - json["test"] = type.name; - json["sysid"] = true; - - // Write the new file with "_new" appended to it. - path.remove_suffix(std::string_view{".json"}.size()); - std::string loc = fmt::format("{}_new.json", path); - - sysid::SaveFile(json.dump(2), std::filesystem::path{loc}); - - WPI_INFO(logger, "Wrote new JSON to: {}", loc); - return loc; -} - -std::string sysid::ToCSV(std::string_view path, wpi::Logger& logger) { - wpi::json json = GetJSON(path, logger); - - auto type = sysid::analysis::FromName(json.at("test").get()); - auto factor = json.at("unitsPerRotation").get(); - auto unit = json.at("units").get(); - std::string_view abbreviation = GetAbbreviation(unit); - - std::error_code ec; - // Naming: {sysid-json-name}(Test, Units).csv - path.remove_suffix(std::string_view{".json"}.size()); - std::string loc = fmt::format("{} ({}, {}).csv", path, type.name, unit); - wpi::raw_fd_ostream outputFile{loc, ec}; - - if (ec) { - throw std::runtime_error("Unable to write to: " + loc); - } - - fmt::print(outputFile, "Timestamp (s),Test,"); - if (type == analysis::kDrivetrain || type == analysis::kDrivetrainAngular) { - fmt::print( - outputFile, - "Left Volts (V),Right Volts (V),Left Position ({0}),Right " - "Position ({0}),Left Velocity ({0}/s),Right Velocity ({0}/s),Gyro " - "Position (deg),Gyro Rate (deg/s)\n", - abbreviation); - } else { - fmt::print(outputFile, "Volts (V),Position({0}),Velocity ({0}/s)\n", - abbreviation); - } - outputFile << "\n"; - - for (auto&& key : AnalysisManager::kJsonDataKeys) { - if (type == analysis::kDrivetrain || type == analysis::kDrivetrainAngular) { - auto tempData = - json.at(key).get>>(); - for (auto&& pt : tempData) { - fmt::print(outputFile, "{},{},{},{},{},{},{},{},{},{}\n", - pt[0], // Timestamp - key, // Test - pt[1], pt[2], // Left and Right Voltages - pt[3] * factor, pt[4] * factor, // Left and Right Positions - pt[5] * factor, pt[6] * factor, // Left and Right Velocity - pt[7], pt[8] // Gyro Position and Velocity - ); - } - } else { - auto tempData = - json.at(key).get>>(); - for (auto&& pt : tempData) { - fmt::print(outputFile, "{},{},{},{},{}\n", - pt[0], // Timestamp, - key, // Test - pt[1], // Voltage - pt[2] * factor, // Position - pt[3] * factor // Velocity - ); - } - } - } - outputFile.flush(); - WPI_INFO(logger, "Wrote CSV to: {}", loc); - return loc; -} diff --git a/sysid/src/main/native/cpp/analysis/OLS.cpp b/sysid/src/main/native/cpp/analysis/OLS.cpp index 5d630d0fa0a..254898bffb0 100644 --- a/sysid/src/main/native/cpp/analysis/OLS.cpp +++ b/sysid/src/main/native/cpp/analysis/OLS.cpp @@ -60,11 +60,11 @@ OLSResult OLS(const Eigen::MatrixXd& X, const Eigen::VectorXd& y) { // // SSTO = yᵀy - 1/n yᵀJy // - // Let J = I. - // - // SSTO = yᵀy - 1/n yᵀy - // SSTO = (n − 1)/n yᵀy - double SSTO = (n - 1.0) / n * (y.transpose() * y).value(); + // where J is a matrix of ones. + double SSTO = + (y.transpose() * y - 1.0 / y.rows() * y.transpose() * + Eigen::MatrixXd::Ones(y.rows(), y.rows()) * y) + .value(); // R² or the coefficient of determination, which represents how much of the // total variation (variation in y) can be explained by the regression model diff --git a/sysid/src/main/native/cpp/telemetry/TelemetryManager.cpp b/sysid/src/main/native/cpp/telemetry/TelemetryManager.cpp deleted file mode 100644 index ac97cdbca4d..00000000000 --- a/sysid/src/main/native/cpp/telemetry/TelemetryManager.cpp +++ /dev/null @@ -1,275 +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 "sysid/telemetry/TelemetryManager.h" - -#include -#include // for ::tolower -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "sysid/Util.h" -#include "sysid/analysis/AnalysisType.h" - -using namespace sysid; - -TelemetryManager::TelemetryManager(const Settings& settings, - wpi::Logger& logger, - nt::NetworkTableInstance instance) - : m_settings(settings), m_logger(logger), m_inst(instance) {} - -void TelemetryManager::BeginTest(std::string_view name) { - // Create a new test params instance for this test. - m_params = - TestParameters{name.starts_with("fast"), name.ends_with("forward"), - m_settings.mechanism == analysis::kDrivetrainAngular, - State::WaitingForEnable}; - - // Add this test to the list of running tests and set the running flag. - m_tests.push_back(std::string{name}); - m_isRunningTest = true; - - // Set the Voltage Command Entry - m_voltageCommand.Set((m_params.fast ? m_settings.stepVoltage - : m_settings.quasistaticRampRate) * - (m_params.forward ? 1 : -1)); - - // Set the test type - m_testType.Set(m_params.fast ? "Dynamic" : "Quasistatic"); - - // Set the rotate entry - m_rotate.Set(m_params.rotate); - - // Set the current mechanism in NT. - m_mechanism.Set(m_settings.mechanism.name); - // Set Overflow to False - m_overflowPub.Set(false); - // Set Mechanism Error to False - m_mechErrorPub.Set(false); - m_inst.Flush(); - - // Display the warning message. - for (auto&& func : m_callbacks) { - func( - "Please enable the robot in autonomous mode, and then " - "disable it " - "before it runs out of space. \n Note: The robot will " - "continue " - "to move until you disable it - It is your " - "responsibility to " - "ensure it does not hit anything!"); - } - - WPI_INFO(m_logger, "Started {} test.", m_tests.back()); -} - -void TelemetryManager::EndTest() { - // If there is no test running, this is a no-op - if (!m_isRunningTest) { - return; - } - - // Disable the running flag and store the data in the JSON. - m_isRunningTest = false; - m_data[m_tests.back()] = m_params.data; - - // Call the cancellation callbacks. - for (auto&& func : m_callbacks) { - std::string msg; - if (m_params.mechError) { - msg += - "\nERROR: The robot indicated that you are using the wrong project " - "for characterizing your mechanism. \nThis most likely means you " - "are trying to characterize a mechanism like a Drivetrain with a " - "deployed config for a General Mechanism (e.g. Arm, Flywheel, and " - "Elevator) or vice versa. Please double check your settings and " - "try again."; - } else if (!m_params.data.empty()) { - std::string units = m_settings.units; - std::transform(m_settings.units.begin(), m_settings.units.end(), - units.begin(), ::tolower); - - if (std::string_view{m_settings.mechanism.name}.starts_with( - "Drivetrain")) { - double p = (m_params.data.back()[3] - m_params.data.front()[3]) * - m_settings.unitsPerRotation; - double s = (m_params.data.back()[4] - m_params.data.front()[4]) * - m_settings.unitsPerRotation; - double g = m_params.data.back()[7] - m_params.data.front()[7]; - - msg = fmt::format( - "The left and right encoders traveled {} {} and {} {} " - "respectively.\nThe gyro angle delta was {} degrees.", - p, units, s, units, g * 180.0 / std::numbers::pi); - } else { - double p = (m_params.data.back()[2] - m_params.data.front()[2]) * - m_settings.unitsPerRotation; - msg = fmt::format("The encoder reported traveling {} {}.", p, units); - } - - if (m_params.overflow) { - msg += - "\nNOTE: the robot stopped recording data early because the entry " - "storage was exceeded."; - } - } else { - msg = "No data was detected."; - } - func(msg); - } - - // Remove previously run test from list of tests if no data was detected. - if (m_params.data.empty()) { - m_tests.pop_back(); - } - - // Send a zero command over NT. - m_voltageCommand.Set(0.0); - m_inst.Flush(); -} - -void TelemetryManager::Update() { - // If there is no test running, these is nothing to update. - if (!m_isRunningTest) { - return; - } - - // Update the NT entries that we're reading. - - int currAckNumber = m_ackNumberSub.Get(); - std::string telemetryValue; - - // Get the FMS Control Word. - for (auto tsValue : m_fmsControlData.ReadQueue()) { - uint32_t ctrl = tsValue.value; - m_params.enabled = ctrl & 0x01; - } - - // Get the string in the data field. - for (auto tsValue : m_telemetry.ReadQueue()) { - telemetryValue = tsValue.value; - } - - // Get the overflow flag - for (auto tsValue : m_overflowSub.ReadQueue()) { - m_params.overflow = tsValue.value; - } - - // Get the mechanism error flag - for (auto tsValue : m_mechErrorSub.ReadQueue()) { - m_params.mechError = tsValue.value; - } - - // Go through our state machine. - if (m_params.state == State::WaitingForEnable) { - if (m_params.enabled) { - m_params.enableStart = wpi::Now() * 1E-6; - m_params.state = State::RunningTest; - m_ackNumber = currAckNumber; - WPI_INFO(m_logger, "{}", "Transitioned to running test state."); - } - } - - if (m_params.state == State::RunningTest) { - // If for some reason we've disconnected, end the test. - if (!m_inst.IsConnected()) { - WPI_WARNING(m_logger, "{}", - "NT connection was dropped when executing the test. The test " - "has been canceled."); - EndTest(); - } - - // If the robot has disabled, then we can move on to the next step. - if (!m_params.enabled) { - m_params.disableStart = wpi::Now() * 1E-6; - m_params.state = State::WaitingForData; - WPI_INFO(m_logger, "{}", "Transitioned to waiting for data."); - } - } - - if (m_params.state == State::WaitingForData) { - double now = wpi::Now() * 1E-6; - m_voltageCommand.Set(0.0); - m_inst.Flush(); - - // Process valid data - if (!telemetryValue.empty() && m_ackNumber < currAckNumber) { - m_params.raw = std::move(telemetryValue); - m_ackNumber = currAckNumber; - } - - // We have the data that we need, so we can parse it and end the test. - if (!m_params.raw.empty() && - wpi::starts_with(m_params.raw, m_tests.back())) { - // Remove test type from start of string - m_params.raw.erase(0, m_params.raw.find(';') + 1); - - // Clean up the string -- remove spaces if there are any. - m_params.raw.erase( - std::remove_if(m_params.raw.begin(), m_params.raw.end(), ::isspace), - m_params.raw.end()); - - // Split the string into individual components. - wpi::SmallVector res; - wpi::split(m_params.raw, res, ','); - - // Convert each string to double. - std::vector values; - values.reserve(res.size()); - for (auto&& str : res) { - values.push_back(wpi::parse_float(str).value()); - } - - // Add the values to our result vector. - for (size_t i = 0; i < values.size() - m_settings.mechanism.rawDataSize; - i += m_settings.mechanism.rawDataSize) { - std::vector d(m_settings.mechanism.rawDataSize); - - std::copy_n(std::make_move_iterator(values.begin() + i), - m_settings.mechanism.rawDataSize, d.begin()); - m_params.data.push_back(std::move(d)); - } - - WPI_INFO(m_logger, - "Received data with size: {} for the {} test in {} seconds.", - m_params.data.size(), m_tests.back(), - m_params.data.back()[0] - m_params.data.front()[0]); - m_ackNumberPub.Set(++m_ackNumber); - EndTest(); - } - - // If we timed out, end the test and let the user know. - if (now - m_params.disableStart > 5.0) { - WPI_WARNING(m_logger, "{}", - "TelemetryManager did not receieve data 5 seconds after " - "completing the test..."); - EndTest(); - } - } -} - -std::string TelemetryManager::SaveJSON(std::string_view location) { - m_data["test"] = m_settings.mechanism.name; - m_data["units"] = m_settings.units; - m_data["unitsPerRotation"] = m_settings.unitsPerRotation; - m_data["sysid"] = true; - - std::string loc = fmt::format("{}/sysid_data{:%Y%m%d-%H%M%S}.json", location, - std::chrono::system_clock::now()); - - sysid::SaveFile(m_data.dump(2), std::filesystem::path{loc}); - WPI_INFO(m_logger, "Wrote JSON to: {}", loc); - - return loc; -} diff --git a/sysid/src/main/native/cpp/view/Analyzer.cpp b/sysid/src/main/native/cpp/view/Analyzer.cpp index ed73e4bf33c..c0bbbae956d 100644 --- a/sysid/src/main/native/cpp/view/Analyzer.cpp +++ b/sysid/src/main/native/cpp/view/Analyzer.cpp @@ -28,7 +28,7 @@ using namespace sysid; Analyzer::Analyzer(glass::Storage& storage, wpi::Logger& logger) - : m_location(""), m_logger(logger) { + : m_logger(logger) { // Fill the StringMap with preset values. m_presets["Default"] = presets::kDefault; m_presets["WPILib (2020-)"] = presets::kWPILibNew; @@ -48,22 +48,20 @@ Analyzer::Analyzer(glass::Storage& storage, wpi::Logger& logger) void Analyzer::UpdateFeedforwardGains() { WPI_INFO(m_logger, "{}", "Gain calc"); try { - const auto& [ff, trackWidth] = m_manager->CalculateFeedforward(); - m_ff = ff.coeffs; - m_accelRSquared = ff.rSquared; - m_accelRMSE = ff.rmse; - m_trackWidth = trackWidth; + const auto& feedforwardGains = m_manager->CalculateFeedforward(); + m_feedforwardGains = feedforwardGains; + m_accelRSquared = feedforwardGains.olsResult.rSquared; + m_accelRMSE = feedforwardGains.olsResult.rmse; m_settings.preset.measurementDelay = m_settings.type == FeedbackControllerLoopType::kPosition ? m_manager->GetPositionDelay() : m_manager->GetVelocityDelay(); - m_conversionFactor = m_manager->GetFactor(); PrepareGraphs(); } catch (const sysid::InvalidDataError& e) { m_state = AnalyzerState::kGeneralDataError; HandleError(e.what()); } catch (const sysid::NoQuasistaticDataError& e) { - m_state = AnalyzerState::kMotionThresholdError; + m_state = AnalyzerState::kVelocityThresholdError; HandleError(e.what()); } catch (const sysid::NoDynamicDataError& e) { m_state = AnalyzerState::kTestDurationError; @@ -81,16 +79,23 @@ void Analyzer::UpdateFeedforwardGains() { } void Analyzer::UpdateFeedbackGains() { - if (m_ff[1] > 0 && m_ff[2] > 0) { - const auto& fb = m_manager->CalculateFeedback(m_ff); - m_timescale = units::second_t{m_ff[2] / m_ff[1]}; + WPI_INFO(m_logger, "{}", "Updating feedback gains"); + + const auto& Kv = m_feedforwardGains.Kv; + const auto& Ka = m_feedforwardGains.Ka; + if (Kv.isValidGain && Ka.isValidGain) { + const auto& fb = m_manager->CalculateFeedback(Kv, Ka); + m_timescale = units::second_t{Ka.gain / Kv.gain}; + m_timescaleValid = true; m_Kp = fb.Kp; m_Kd = fb.Kd; + } else { + m_timescaleValid = false; } } -bool Analyzer::DisplayGain(const char* text, double* data, - bool readOnly = true) { +bool Analyzer::DisplayDouble(const char* text, double* data, + bool readOnly = true) { ImGui::SetNextItemWidth(ImGui::GetFontSize() * 5); if (readOnly) { return ImGui::InputDouble(text, data, 0.0, 0.0, "%.5G", @@ -107,40 +112,22 @@ static void SetPosition(double beginX, double beginY, double xShift, } bool Analyzer::IsErrorState() { - return m_state == AnalyzerState::kMotionThresholdError || + return m_state == AnalyzerState::kVelocityThresholdError || m_state == AnalyzerState::kTestDurationError || m_state == AnalyzerState::kGeneralDataError || m_state == AnalyzerState::kFileError; } bool Analyzer::IsDataErrorState() { - return m_state == AnalyzerState::kMotionThresholdError || + return m_state == AnalyzerState::kVelocityThresholdError || m_state == AnalyzerState::kTestDurationError || m_state == AnalyzerState::kGeneralDataError; } -void Analyzer::DisplayFileSelector() { - // Get the current width of the window. This will be used to scale - // our UI elements. - float width = ImGui::GetContentRegionAvail().x; - - // Show the file location along with an option to choose. - if (ImGui::Button("Select")) { - m_selector = std::make_unique( - "Select Data", "", - std::vector{"JSON File", SYSID_PFD_JSON_EXT}); - } - ImGui::SameLine(); - ImGui::SetNextItemWidth(width - ImGui::CalcTextSize("Select").x - - ImGui::GetFontSize() * 5); - ImGui::InputText("##location", &m_location, ImGuiInputTextFlags_ReadOnly); -} - void Analyzer::ResetData() { m_plot.ResetData(); m_manager = std::make_unique(m_settings, m_logger); - m_location = ""; - m_ff = std::vector{1, 1, 1}; + m_feedforwardGains = AnalysisManager::FeedforwardGains{}; UpdateFeedbackGains(); } @@ -152,38 +139,15 @@ bool Analyzer::DisplayResetAndUnitOverride() { ImGui::SameLine(width - ImGui::CalcTextSize("Reset").x); if (ImGui::Button("Reset")) { ResetData(); - m_state = AnalyzerState::kWaitingForJSON; + m_state = AnalyzerState::kWaitingForData; return true; } - if (type == analysis::kDrivetrain) { - ImGui::SetNextItemWidth(ImGui::GetFontSize() * kTextBoxWidthMultiple); - if (ImGui::Combo("Dataset", &m_dataset, kDatasets, 3)) { - m_settings.dataset = - static_cast(m_dataset); - PrepareData(); - } - ImGui::SameLine(); - } else { - m_settings.dataset = - AnalysisManager::Settings::DrivetrainDataset::kCombined; - } - ImGui::Spacing(); ImGui::Text( "Units: %s\n" - "Units Per Rotation: %.4f\n" "Type: %s", - std::string(unit).c_str(), m_conversionFactor, type.name); - - if (type == analysis::kDrivetrainAngular) { - ImGui::SameLine(); - sysid::CreateTooltip( - "Here, the units and units per rotation represent what the wheel " - "positions and velocities were captured in. The track width value " - "will reflect the unit selected here. However, the Kv and Ka will " - "always be in Vs/rad and Vs^2 / rad respectively."); - } + std::string(unit).c_str(), type.name); if (ImGui::Button("Override Units")) { ImGui::OpenPopup("Override Units"); @@ -197,24 +161,11 @@ bool Analyzer::DisplayResetAndUnitOverride() { IM_ARRAYSIZE(kUnits)); unit = kUnits[m_selectedOverrideUnit]; - if (unit == "Degrees") { - m_conversionFactor = 360.0; - } else if (unit == "Radians") { - m_conversionFactor = 2 * std::numbers::pi; - } else if (unit == "Rotations") { - m_conversionFactor = 1.0; - } - - bool isRotational = m_selectedOverrideUnit > 2; - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 7); - ImGui::InputDouble( - "Units Per Rotation", &m_conversionFactor, 0.0, 0.0, "%.4f", - isRotational ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_None); if (ImGui::Button("Close")) { ImGui::CloseCurrentPopup(); - m_manager->OverrideUnits(unit, m_conversionFactor); + m_manager->OverrideUnits(unit); PrepareData(); } @@ -234,22 +185,22 @@ void Analyzer::ConfigParamsOnFileSelect() { WPI_INFO(m_logger, "{}", "Configuring Params"); m_stepTestDuration = m_settings.stepTestDuration.to(); - // Estimate qp as 1/8 * units-per-rot - m_settings.lqr.qp = 0.125 * m_manager->GetFactor(); + // Estimate qp as 1/10 native distance unit + m_settings.lqr.qp = 0.1; // Estimate qv as 1/4 * max velocity = 1/4 * (12V - kS) / kV - m_settings.lqr.qv = 0.25 * (12.0 - m_ff[0]) / m_ff[1]; + m_settings.lqr.qv = + 0.25 * (12.0 - m_feedforwardGains.Ks.gain) / m_feedforwardGains.Kv.gain; } void Analyzer::Display() { - DisplayFileSelector(); DisplayGraphs(); switch (m_state) { - case AnalyzerState::kWaitingForJSON: { + case AnalyzerState::kWaitingForData: { ImGui::Text( "SysId is currently in theoretical analysis mode.\n" "To analyze recorded test data, select a " - "data JSON."); + "data file (.wpilog)."); sysid::CreateTooltip( "Theoretical feedback gains can be calculated from a " "physical model of the mechanism being controlled. " @@ -295,14 +246,14 @@ void Analyzer::Display() { case AnalyzerState::kFileError: { CreateErrorPopup(m_errorPopup, m_exception); if (!m_errorPopup) { - m_state = AnalyzerState::kWaitingForJSON; + m_state = AnalyzerState::kWaitingForData; return; } break; } case AnalyzerState::kGeneralDataError: case AnalyzerState::kTestDurationError: - case AnalyzerState::kMotionThresholdError: { + case AnalyzerState::kVelocityThresholdError: { CreateErrorPopup(m_errorPopup, m_exception); if (DisplayResetAndUnitOverride()) { return; @@ -313,20 +264,10 @@ void Analyzer::Display() { break; } } - - // Periodic functions - try { - SelectFile(); - } catch (const AnalysisManager::FileReadingError& e) { - m_state = AnalyzerState::kFileError; - HandleError(e.what()); - } catch (const wpi::json::exception& e) { - m_state = AnalyzerState::kFileError; - HandleError(e.what()); - } } void Analyzer::PrepareData() { + WPI_INFO(m_logger, "{}", "Preparing data"); try { m_manager->PrepareData(); UpdateFeedforwardGains(); @@ -335,7 +276,7 @@ void Analyzer::PrepareData() { m_state = AnalyzerState::kGeneralDataError; HandleError(e.what()); } catch (const sysid::NoQuasistaticDataError& e) { - m_state = AnalyzerState::kMotionThresholdError; + m_state = AnalyzerState::kVelocityThresholdError; HandleError(e.what()); } catch (const sysid::NoDynamicDataError& e) { m_state = AnalyzerState::kTestDurationError; @@ -368,8 +309,9 @@ void Analyzer::PrepareGraphs() { AbortDataPrep(); m_dataThread = std::thread([&] { m_plot.SetData(m_manager->GetRawData(), m_manager->GetFilteredData(), - m_manager->GetUnit(), m_ff, m_manager->GetStartTimes(), - m_manager->GetAnalysisType(), m_abortDataPrep); + m_manager->GetUnit(), m_feedforwardGains, + m_manager->GetStartTimes(), m_manager->GetAnalysisType(), + m_abortDataPrep); }); UpdateFeedbackGains(); m_state = AnalyzerState::kNominalDisplay; @@ -379,9 +321,6 @@ void Analyzer::PrepareGraphs() { void Analyzer::HandleError(std::string_view msg) { m_exception = msg; m_errorPopup = true; - if (m_state == AnalyzerState::kFileError) { - m_location = ""; - } PrepareRawGraphs(); } @@ -424,28 +363,28 @@ void Analyzer::DisplayGraphs() { // If a JSON is selected if (m_state == AnalyzerState::kNominalDisplay) { - DisplayGain("Acceleration R²", &m_accelRSquared); + DisplayDouble("Acceleration R²", &m_accelRSquared); CreateTooltip( "The coefficient of determination of the OLS fit of acceleration " "versus velocity and voltage. Acceleration is extremely noisy, " "so this is generally quite small."); ImGui::SameLine(); - DisplayGain("Acceleration RMSE", &m_accelRMSE); + DisplayDouble("Acceleration RMSE", &m_accelRMSE); CreateTooltip( "The standard deviation of the residuals from the predicted " "acceleration." "This can be interpreted loosely as the mean measured disturbance " "from the \"ideal\" system equation."); - DisplayGain("Sim velocity R²", m_plot.GetSimRSquared()); + DisplayDouble("Sim velocity R²", m_plot.GetSimRSquared()); CreateTooltip( "The coefficient of determination the simulated velocity. " "Velocity is much less-noisy than acceleration, so this " "is pretty close to 1 for a decent fit."); ImGui::SameLine(); - DisplayGain("Sim velocity RMSE", m_plot.GetSimRMSE()); + DisplayDouble("Sim velocity RMSE", m_plot.GetSimRMSE()); CreateTooltip( "The standard deviation of the residuals from the simulated velocity " "predictions - essentially the size of the mean error of the " @@ -458,23 +397,12 @@ void Analyzer::DisplayGraphs() { ImGui::End(); } -void Analyzer::SelectFile() { - // If the selector exists and is ready with a result, we can store it. - if (m_selector && m_selector->ready() && !m_selector->result().empty()) { - // Store the location of the file and reset the selector. - WPI_INFO(m_logger, "Opening File: {}", m_selector->result()[0]); - m_location = m_selector->result()[0]; - m_selector.reset(); - WPI_INFO(m_logger, "{}", "Opened File"); - m_manager = - std::make_unique(m_location, m_settings, m_logger); - PrepareData(); - m_dataset = 0; - m_settings.dataset = - AnalysisManager::Settings::DrivetrainDataset::kCombined; - ConfigParamsOnFileSelect(); - UpdateFeedbackGains(); - } +void Analyzer::AnalyzeData() { + m_manager = std::make_unique(m_data, m_settings, m_logger); + PrepareData(); + m_dataset = 0; + ConfigParamsOnFileSelect(); + UpdateFeedbackGains(); } void Analyzer::AbortDataPrep() { @@ -487,7 +415,7 @@ void Analyzer::AbortDataPrep() { void Analyzer::DisplayFeedforwardParameters(float beginX, float beginY) { // Increase spacing to not run into trackwidth in the normal analyzer view - constexpr double kHorizontalOffset = 0.9; + constexpr double kHorizontalOffset = 1.1; SetPosition(beginX, beginY, kHorizontalOffset, 0); bool displayAll = @@ -509,15 +437,15 @@ void Analyzer::DisplayFeedforwardParameters(float beginX, float beginY) { "filter's sliding window."); } - if (displayAll || m_state == AnalyzerState::kMotionThresholdError) { + if (displayAll || m_state == AnalyzerState::kVelocityThresholdError) { // Wait for enter before refresh so decimal inputs like "0.2" don't // prematurely refresh with a velocity threshold of "0". SetPosition(beginX, beginY, kHorizontalOffset, 1); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4); - double threshold = m_settings.motionThreshold; + double threshold = m_settings.velocityThreshold; if (ImGui::InputDouble("Velocity Threshold", &threshold, 0.0, 0.0, "%.3f", ImGuiInputTextFlags_EnterReturnsTrue)) { - m_settings.motionThreshold = std::max(0.0, threshold); + m_settings.velocityThreshold = std::max(0.0, threshold); PrepareData(); } CreateTooltip("Velocity data below this threshold will be ignored."); @@ -537,20 +465,20 @@ void Analyzer::DisplayFeedforwardParameters(float beginX, float beginY) { void Analyzer::CollectFeedforwardGains(float beginX, float beginY) { SetPosition(beginX, beginY, 0, 0); - if (DisplayGain("Kv", &m_ff[1], false)) { + if (DisplayDouble("Kv", &m_feedforwardGains.Kv.gain, false)) { UpdateFeedbackGains(); } SetPosition(beginX, beginY, 0, 1); - if (DisplayGain("Ka", &m_ff[2], false)) { + if (DisplayDouble("Ka", &m_feedforwardGains.Ka.gain, false)) { UpdateFeedbackGains(); } SetPosition(beginX, beginY, 0, 2); // Show Timescale ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4); - DisplayGain("Response Timescale (ms)", - reinterpret_cast(&m_timescale)); + DisplayDouble("Response Timescale (ms)", + reinterpret_cast(&m_timescale)); CreateTooltip( "The characteristic timescale of the system response in milliseconds. " "Both the control loop period and total signal delay should be " @@ -558,21 +486,39 @@ void Analyzer::CollectFeedforwardGains(float beginX, float beginY) { "system."); } +void Analyzer::DisplayFeedforwardGain(const char* text, + AnalysisManager::FeedforwardGain& ffGain, + bool readOnly = true) { + DisplayDouble(text, &ffGain.gain, readOnly); + if (!ffGain.isValidGain) { + // Display invalid gain message with warning and tooltip + CreateErrorTooltip(ffGain.errorMessage.c_str()); + } + + // Display descriptor message as tooltip, whether the gain is valid or not + CreateTooltip(ffGain.descriptor.c_str()); +} + void Analyzer::DisplayFeedforwardGains(float beginX, float beginY) { SetPosition(beginX, beginY, 0, 0); - DisplayGain("Ks", &m_ff[0]); + DisplayFeedforwardGain("Ks", m_feedforwardGains.Ks); SetPosition(beginX, beginY, 0, 1); - DisplayGain("Kv", &m_ff[1]); + DisplayFeedforwardGain("Kv", m_feedforwardGains.Kv); SetPosition(beginX, beginY, 0, 2); - DisplayGain("Ka", &m_ff[2]); + DisplayFeedforwardGain("Ka", m_feedforwardGains.Ka); SetPosition(beginX, beginY, 0, 3); // Show Timescale ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4); - DisplayGain("Response Timescale (ms)", - reinterpret_cast(&m_timescale)); + DisplayDouble("Response Timescale (ms)", + reinterpret_cast(&m_timescale)); + if (!m_timescaleValid) { + CreateErrorTooltip( + "Response timescale calculation invalid. Ensure that calculated gains " + "are valid."); + } CreateTooltip( "The characteristic timescale of the system response in milliseconds. " "Both the control loop period and total signal delay should be " @@ -581,8 +527,8 @@ void Analyzer::DisplayFeedforwardGains(float beginX, float beginY) { SetPosition(beginX, beginY, 0, 4); auto positionDelay = m_manager->GetPositionDelay(); - DisplayGain("Position Measurement Delay (ms)", - reinterpret_cast(&positionDelay)); + DisplayDouble("Position Measurement Delay (ms)", + reinterpret_cast(&positionDelay)); CreateTooltip( "The average elapsed time between the first application of " "voltage and the first detected change in mechanism position " @@ -592,8 +538,8 @@ void Analyzer::DisplayFeedforwardGains(float beginX, float beginY) { SetPosition(beginX, beginY, 0, 5); auto velocityDelay = m_manager->GetVelocityDelay(); - DisplayGain("Velocity Measurement Delay (ms)", - reinterpret_cast(&velocityDelay)); + DisplayDouble("Velocity Measurement Delay (ms)", + reinterpret_cast(&velocityDelay)); CreateTooltip( "The average elapsed time between the first application of " "voltage and the maximum calculated mechanism acceleration " @@ -604,20 +550,20 @@ void Analyzer::DisplayFeedforwardGains(float beginX, float beginY) { SetPosition(beginX, beginY, 0, 6); if (m_manager->GetAnalysisType() == analysis::kElevator) { - DisplayGain("Kg", &m_ff[3]); + DisplayFeedforwardGain("Kg", m_feedforwardGains.Kg); } else if (m_manager->GetAnalysisType() == analysis::kArm) { - DisplayGain("Kg", &m_ff[3]); + DisplayFeedforwardGain("Kg", m_feedforwardGains.Kg); double offset; auto unit = m_manager->GetUnit(); if (unit == "Radians") { - offset = m_ff[4]; + offset = m_feedforwardGains.offset.gain; } else if (unit == "Degrees") { - offset = m_ff[4] / std::numbers::pi * 180.0; + offset = m_feedforwardGains.offset.gain / std::numbers::pi * 180.0; } else if (unit == "Rotations") { - offset = m_ff[4] / (2 * std::numbers::pi); + offset = m_feedforwardGains.offset.gain / (2 * std::numbers::pi); } - DisplayGain( + DisplayDouble( fmt::format("Angle offset to horizontal ({})", GetAbbreviation(unit)) .c_str(), &offset); @@ -625,8 +571,6 @@ void Analyzer::DisplayFeedforwardGains(float beginX, float beginY) { "This is the angle offset which, when added to the angle measurement, " "zeroes it out when the arm is horizontal. This is needed for the arm " "feedforward to work."); - } else if (m_trackWidth) { - DisplayGain("Track Width", &*m_trackWidth); } double endY = ImGui::GetCursorPosY(); @@ -644,7 +588,6 @@ void Analyzer::DisplayFeedbackGains() { m_settings.type = FeedbackControllerLoopType::kVelocity; m_selectedLoopType = static_cast(FeedbackControllerLoopType::kVelocity); - m_settings.convertGainsToEncTicks = m_selectedPreset > 2; UpdateFeedbackGains(); } ImGui::SameLine(); @@ -728,59 +671,6 @@ void Analyzer::DisplayFeedbackGains() { "accurate if the characteristic timescale of the mechanism " "is small."); - // Add CPR and Gearing for converting Feedback Gains - ImGui::Separator(); - ImGui::Spacing(); - - if (ImGui::Checkbox("Convert Gains to Encoder Counts", - &m_settings.convertGainsToEncTicks)) { - UpdateFeedbackGains(); - } - sysid::CreateTooltip( - "Whether the feedback gains should be in terms of encoder counts or " - "output units. Because smart motor controllers usually don't have " - "direct access to the output units (i.e. m/s for a drivetrain), they " - "perform feedback on the encoder counts directly. If you are using a " - "PID Controller on the RoboRIO, you are probably performing feedback " - "on the output units directly.\n\nNote that if you have properly set " - "up position and velocity conversion factors with the SPARK MAX, you " - "can leave this box unchecked. The motor controller will perform " - "feedback on the output directly."); - - if (m_settings.convertGainsToEncTicks) { - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 5); - if (ImGui::InputDouble("##Numerator", &m_gearingNumerator, 0.0, 0.0, "%.4f", - ImGuiInputTextFlags_EnterReturnsTrue) && - m_gearingNumerator > 0) { - m_settings.gearing = m_gearingNumerator / m_gearingDenominator; - UpdateFeedbackGains(); - } - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 5); - if (ImGui::InputDouble("##Denominator", &m_gearingDenominator, 0.0, 0.0, - "%.4f", ImGuiInputTextFlags_EnterReturnsTrue) && - m_gearingDenominator > 0) { - m_settings.gearing = m_gearingNumerator / m_gearingDenominator; - UpdateFeedbackGains(); - } - sysid::CreateTooltip( - "The gearing between the encoder and the motor shaft (# of encoder " - "turns / # of motor shaft turns)."); - - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 5); - if (ImGui::InputInt("CPR", &m_settings.cpr, 0, 0, - ImGuiInputTextFlags_EnterReturnsTrue) && - m_settings.cpr > 0) { - UpdateFeedbackGains(); - } - sysid::CreateTooltip( - "The counts per rotation of your encoder. This is the number of counts " - "reported in user code when the encoder is rotated exactly once. Some " - "common values for various motors/encoders are:\n\n" - "Falcon 500: 2048\nNEO: 1\nCTRE Mag Encoder / CANCoder: 4096\nREV " - "Through Bore Encoder: 8192\n"); - } - ImGui::Separator(); ImGui::Spacing(); @@ -790,7 +680,7 @@ void Analyzer::DisplayFeedbackGains() { IM_ARRAYSIZE(kLoopTypes))) { m_settings.type = static_cast(m_selectedLoopType); - if (m_state == AnalyzerState::kWaitingForJSON) { + if (m_state == AnalyzerState::kWaitingForData) { m_settings.preset.measurementDelay = 0_ms; } else { if (m_settings.type == FeedbackControllerLoopType::kPosition) { @@ -807,23 +697,23 @@ void Analyzer::DisplayFeedbackGains() { // Show Kp and Kd. float beginY = ImGui::GetCursorPosY(); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4); - DisplayGain("Kp", &m_Kp); + DisplayDouble("Kp", &m_Kp); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4); - DisplayGain("Kd", &m_Kd); + DisplayDouble("Kd", &m_Kd); // Come back to the starting y pos. ImGui::SetCursorPosY(beginY); if (m_selectedLoopType == 0) { std::string unit; - if (m_state != AnalyzerState::kWaitingForJSON) { + if (m_state != AnalyzerState::kWaitingForData) { unit = fmt::format(" ({})", GetAbbreviation(m_manager->GetUnit())); } ImGui::SetCursorPosX(ImGui::GetFontSize() * 9); - if (DisplayGain(fmt::format("Max Position Error{}", unit).c_str(), - &m_settings.lqr.qp, false)) { + if (DisplayDouble(fmt::format("Max Position Error{}", unit).c_str(), + &m_settings.lqr.qp, false)) { if (m_settings.lqr.qp > 0) { UpdateFeedbackGains(); } @@ -831,19 +721,19 @@ void Analyzer::DisplayFeedbackGains() { } std::string unit; - if (m_state != AnalyzerState::kWaitingForJSON) { + if (m_state != AnalyzerState::kWaitingForData) { unit = fmt::format(" ({}/s)", GetAbbreviation(m_manager->GetUnit())); } ImGui::SetCursorPosX(ImGui::GetFontSize() * 9); - if (DisplayGain(fmt::format("Max Velocity Error{}", unit).c_str(), - &m_settings.lqr.qv, false)) { + if (DisplayDouble(fmt::format("Max Velocity Error{}", unit).c_str(), + &m_settings.lqr.qv, false)) { if (m_settings.lqr.qv > 0) { UpdateFeedbackGains(); } } ImGui::SetCursorPosX(ImGui::GetFontSize() * 9); - if (DisplayGain("Max Control Effort (V)", &m_settings.lqr.r, false)) { + if (DisplayDouble("Max Control Effort (V)", &m_settings.lqr.r, false)) { if (m_settings.lqr.r > 0) { UpdateFeedbackGains(); } diff --git a/sysid/src/main/native/cpp/view/AnalyzerPlot.cpp b/sysid/src/main/native/cpp/view/AnalyzerPlot.cpp index d8afbaf06c0..6963f2c1b8f 100644 --- a/sysid/src/main/native/cpp/view/AnalyzerPlot.cpp +++ b/sysid/src/main/native/cpp/view/AnalyzerPlot.cpp @@ -127,16 +127,16 @@ void AnalyzerPlot::SetRawData(const Storage& data, std::string_view unit, void AnalyzerPlot::SetData(const Storage& rawData, const Storage& filteredData, std::string_view unit, - const std::vector& ffGains, + const AnalysisManager::FeedforwardGains& ffGains, const std::array& startTimes, AnalysisType type, std::atomic& abort) { double simSquaredErrorSum = 0; double squaredVariationSum = 0; int timeSeriesPoints = 0; - const auto& Ks = ffGains[0]; - const auto& Kv = ffGains[1]; - const auto& Ka = ffGains[2]; + const auto& Ks = ffGains.Ks.gain; + const auto& Kv = ffGains.Kv.gain; + const auto& Ka = ffGains.Ka.gain; auto& [slowForward, slowBackward, fastForward, fastBackward] = filteredData; auto& [rawSlowForward, rawSlowBackward, rawFastForward, rawFastBackward] = @@ -223,7 +223,7 @@ void AnalyzerPlot::SetData(const Storage& rawData, const Storage& filteredData, // Populate simulated time domain data if (type == analysis::kElevator) { - const auto& Kg = ffGains[3]; + const auto& Kg = ffGains.Kg.gain; m_quasistaticData.simData = PopulateTimeDomainSim( rawSlow, startTimes, fastStep, sysid::ElevatorSim{Ks, Kv, Ka, Kg}, &simSquaredErrorSum, &squaredVariationSum, &timeSeriesPoints); @@ -231,8 +231,8 @@ void AnalyzerPlot::SetData(const Storage& rawData, const Storage& filteredData, rawFast, startTimes, fastStep, sysid::ElevatorSim{Ks, Kv, Ka, Kg}, &simSquaredErrorSum, &squaredVariationSum, &timeSeriesPoints); } else if (type == analysis::kArm) { - const auto& Kg = ffGains[3]; - const auto& offset = ffGains[4]; + const auto& Kg = ffGains.Kg.gain; + const auto& offset = ffGains.offset.gain; m_quasistaticData.simData = PopulateTimeDomainSim( rawSlow, startTimes, fastStep, sysid::ArmSim{Ks, Kv, Ka, Kg, offset}, &simSquaredErrorSum, &squaredVariationSum, &timeSeriesPoints); @@ -288,10 +288,10 @@ void AnalyzerPlot::SetData(const Storage& rawData, const Storage& filteredData, std::copysign(Ks / Ka, slow[i].velocity); if (type == analysis::kElevator) { - const auto& Kg = ffGains[3]; + const auto& Kg = ffGains.Kg.gain; accelPortion -= Kg / Ka; } else if (type == analysis::kArm) { - const auto& Kg = ffGains[3]; + const auto& Kg = ffGains.Kg.gain; accelPortion -= Kg / Ka * slow[i].cos; } @@ -307,10 +307,10 @@ void AnalyzerPlot::SetData(const Storage& rawData, const Storage& filteredData, std::copysign(Ks / Ka, fast[i].velocity); if (type == analysis::kElevator) { - const auto& Kg = ffGains[3]; + const auto& Kg = ffGains.Kg.gain; accelPortion -= Kg / Ka; } else if (type == analysis::kArm) { - const auto& Kg = ffGains[3]; + const auto& Kg = ffGains.Kg.gain; accelPortion -= Kg / Ka * fast[i].cos; } diff --git a/sysid/src/main/native/cpp/view/DataSelector.cpp b/sysid/src/main/native/cpp/view/DataSelector.cpp new file mode 100644 index 00000000000..75b12e5e3ab --- /dev/null +++ b/sysid/src/main/native/cpp/view/DataSelector.cpp @@ -0,0 +1,295 @@ +// 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 "sysid/view/DataSelector.h" + +#include +#include +#include +#include +#include +#include + +#include "sysid/Util.h" +#include "sysid/analysis/AnalysisType.h" +#include "sysid/analysis/Storage.h" + +using namespace sysid; + +static constexpr const char* kAnalysisTypes[] = {"Elevator", "Arm", "Simple"}; + +static bool EmitEntryTarget(const char* name, bool isString, + const glass::DataLogReaderEntry** entry) { + if (*entry) { + auto text = + fmt::format("{}: {} ({})", name, (*entry)->name, (*entry)->type); + ImGui::TextUnformatted(text.c_str()); + } else { + ImGui::Text("%s: (%s)", name, + isString ? "string" : "number"); + } + bool rv = false; + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload( + isString ? "DataLogEntryString" : "DataLogEntry")) { + assert(payload->DataSize == sizeof(const glass::DataLogReaderEntry*)); + *entry = *static_cast(payload->Data); + rv = true; + } + ImGui::EndDragDropTarget(); + } + return rv; +} + +void DataSelector::Display() { + using namespace std::chrono_literals; + + // building test data is modal (due to async access) + if (m_testdataFuture.valid()) { + if (m_testdataFuture.wait_for(0s) == std::future_status::ready) { + TestData data = m_testdataFuture.get(); + for (auto&& motordata : data.motorData) { + m_testdataStats.emplace_back( + fmt::format("Test State: {}", motordata.first())); + int i = 0; + for (auto&& run : motordata.second.runs) { + m_testdataStats.emplace_back(fmt::format( + " Run {} samples: {} Volt {} Pos {} Vel", ++i, + run.voltage.size(), run.position.size(), run.velocity.size())); + } + } + if (testdata) { + testdata(std::move(data)); + } + } + ImGui::Text("Loading data..."); + return; + } + + if (!m_testdataStats.empty()) { + for (auto&& line : m_testdataStats) { + ImGui::TextUnformatted(line.c_str()); + } + if (ImGui::Button("Ok")) { + m_testdataStats.clear(); + } + return; + } + + if (EmitEntryTarget("Test State", true, &m_testStateEntry)) { + m_testsFuture = + std::async(std::launch::async, [testStateEntry = m_testStateEntry] { + return LoadTests(*testStateEntry); + }); + } + + if (!m_testStateEntry) { + return; + } + + if (m_testsFuture.valid() && + m_testsFuture.wait_for(0s) == std::future_status::ready) { + m_tests = m_testsFuture.get(); + for (auto it = m_tests.begin(); it != m_tests.end();) { + if (it->first != "quasistatic" && it->first != "dynamic") { + WPI_WARNING(m_logger, "Unrecognized test {}, removing", it->first); + it = m_tests.erase(it); + continue; + } + for (auto it2 = it->second.begin(); it2 != it->second.end();) { + auto direction = wpi::rsplit(it2->first, '-').second; + if (direction != "forward" && direction != "reverse") { + WPI_WARNING(m_logger, "Unrecognized direction {}, removing", + direction); + it2 = it->second.erase(it2); + continue; + } + WPI_INFO(m_logger, "Loaded test state {}", it2->first); + ++it2; + } + if (it->second.empty()) { + WPI_WARNING(m_logger, "No data for test {}, removing", it->first); + it = m_tests.erase(it); + continue; + } + ++it; + } + WPI_INFO(m_logger, "Loaded {} tests", m_tests.size()); + } + + if (m_tests.empty()) { + if (m_testsFuture.valid()) { + ImGui::TextUnformatted("Reading tests..."); + } else { + ImGui::TextUnformatted("No tests found"); + } + return; + } + +#if 0 + // Test filtering + if (ImGui::BeginCombo("Test", m_selectedTest.c_str())) { + for (auto&& test : m_tests) { + if (ImGui::Selectable(test.first.c_str(), test.first == m_selectedTest)) { + m_selectedTest = test.first; + } + } + ImGui::EndCombo(); + } +#endif + + ImGui::Combo("Analysis Type", &m_selectedAnalysis, kAnalysisTypes, + IM_ARRAYSIZE(kAnalysisTypes)); + + // DND targets + EmitEntryTarget("Velocity", false, &m_velocityEntry); + EmitEntryTarget("Position", false, &m_positionEntry); + EmitEntryTarget("Voltage", false, &m_voltageEntry); + + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 7); + ImGui::Combo("Units", &m_selectedUnit, kUnits, IM_ARRAYSIZE(kUnits)); + + ImGui::InputDouble("Velocity scaling", &m_velocityScale); + ImGui::InputDouble("Position scaling", &m_positionScale); + + if (/*!m_selectedTest.empty() &&*/ m_velocityEntry && m_positionEntry && + m_voltageEntry) { + if (ImGui::Button("Load")) { + m_testdataFuture = + std::async(std::launch::async, [this] { return BuildTestData(); }); + } + } +} + +void DataSelector::Reset() { + m_testsFuture = {}; + m_tests.clear(); + m_selectedTest.clear(); + m_testStateEntry = nullptr; + m_velocityEntry = nullptr; + m_positionEntry = nullptr; + m_voltageEntry = nullptr; + m_testdataFuture = {}; +} + +DataSelector::Tests DataSelector::LoadTests( + const glass::DataLogReaderEntry& testStateEntry) { + Tests tests; + for (auto&& range : testStateEntry.ranges) { + std::string_view prevState; + Runs* curRuns = nullptr; + wpi::log::DataLogReader::iterator lastStart = range.begin(); + int64_t ts = lastStart->GetTimestamp(); + for (auto it = range.begin(), end = range.end(); it != end; ++it) { + ts = it->GetTimestamp(); + std::string_view testState; + if (it->GetEntry() != testStateEntry.entry || + !it->GetString(&testState)) { + continue; + } + + // track runs as iterator ranges of the same test + if (testState != prevState) { + if (curRuns) { + curRuns->emplace_back(lastStart->GetTimestamp(), ts); + } + lastStart = it; + } + prevState = testState; + + if (testState == "none") { + curRuns = nullptr; + continue; + } + + auto [testName, direction] = wpi::rsplit(testState, '-'); + auto testIt = tests.find(testName); + if (testIt == tests.end()) { + testIt = tests.emplace(std::string{testName}, State{}).first; + } + auto stateIt = testIt->second.find(testState); + if (stateIt == testIt->second.end()) { + stateIt = testIt->second.emplace(std::string{testState}, Runs{}).first; + } + curRuns = &stateIt->second; + } + + if (curRuns) { + curRuns->emplace_back(lastStart->GetTimestamp(), ts); + } + } + return tests; +} + +template +static void AddSamples(std::vector>& samples, + const std::vector>& data, + int64_t tsbegin, int64_t tsend) { + // data is sorted, so do a binary search for tsbegin and tsend + auto begin = std::lower_bound( + data.begin(), data.end(), tsbegin, + [](const auto& datapoint, double val) { return datapoint.first < val; }); + auto end = std::lower_bound( + begin, data.end(), tsend, + [](const auto& datapoint, double val) { return datapoint.first < val; }); + + for (auto it = begin; it != end; ++it) { + samples.emplace_back(units::second_t{it->first * 1.0e-6}, T{it->second}); + } +} + +static std::vector> GetData( + const glass::DataLogReaderEntry& entry, double scale) { + std::vector> rv; + bool isDouble = entry.type == "double"; + for (auto&& range : entry.ranges) { + for (auto&& record : range) { + if (record.GetEntry() != entry.entry) { + continue; + } + if (isDouble) { + double val; + if (record.GetDouble(&val)) { + rv.emplace_back(record.GetTimestamp(), val * scale); + } + } else { + float val; + if (record.GetFloat(&val)) { + rv.emplace_back(record.GetTimestamp(), + static_cast(val * scale)); + } + } + } + } + + std::sort(rv.begin(), rv.end(), + [](const auto& a, const auto& b) { return a.first < b.first; }); + return rv; +} + +TestData DataSelector::BuildTestData() { + TestData data; + data.distanceUnit = kUnits[m_selectedUnit]; + data.mechanismType = analysis::FromName(kAnalysisTypes[m_selectedAnalysis]); + + // read and sort the entire dataset first; this is memory hungry but + // dramatically speeds up splitting it into runs. + auto voltageData = GetData(*m_voltageEntry, 1.0); + auto positionData = GetData(*m_positionEntry, m_positionScale); + auto velocityData = GetData(*m_velocityEntry, m_velocityScale); + + for (auto&& test : m_tests) { + for (auto&& state : test.second) { + auto& motorData = data.motorData[state.first]; + for (auto [tsbegin, tsend] : state.second) { + auto& run = motorData.runs.emplace_back(); + AddSamples(run.voltage, voltageData, tsbegin, tsend); + AddSamples(run.position, positionData, tsbegin, tsend); + AddSamples(run.velocity, velocityData, tsbegin, tsend); + } + } + } + + return data; +} diff --git a/sysid/src/main/native/cpp/view/JSONConverter.cpp b/sysid/src/main/native/cpp/view/JSONConverter.cpp deleted file mode 100644 index 88eaa6a02f0..00000000000 --- a/sysid/src/main/native/cpp/view/JSONConverter.cpp +++ /dev/null @@ -1,64 +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 "sysid/analysis/JSONConverter.h" -#include "sysid/view/JSONConverter.h" - -#include - -#include -#include -#include - -#include "sysid/Util.h" - -using namespace sysid; - -void JSONConverter::DisplayConverter( - const char* tooltip, - std::function converter) { - if (ImGui::Button(tooltip)) { - m_opener = std::make_unique( - tooltip, "", std::vector{"JSON File", SYSID_PFD_JSON_EXT}); - } - - if (m_opener && m_opener->ready()) { - if (!m_opener->result().empty()) { - m_location = m_opener->result()[0]; - try { - converter(m_location, m_logger); - m_timestamp = wpi::Now() * 1E-6; - } catch (const std::exception& e) { - ImGui::OpenPopup("Exception Caught!"); - m_exception = e.what(); - } - } - m_opener.reset(); - } - - if (wpi::Now() * 1E-6 - m_timestamp < 5) { - ImGui::SameLine(); - ImGui::Text("Saved!"); - } - - // Handle exceptions. - ImGui::SetNextWindowSize(ImVec2(480.f, 0.0f)); - if (ImGui::BeginPopupModal("Exception Caught!")) { - ImGui::PushTextWrapPos(0.0f); - ImGui::Text( - "An error occurred when parsing the JSON. This most likely means that " - "the JSON data is incorrectly formatted."); - ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%s", - m_exception.c_str()); - ImGui::PopTextWrapPos(); - if (ImGui::Button("Close")) { - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } -} - -void JSONConverter::DisplayCSVConvert() { - DisplayConverter("Select SysId JSON", sysid::ToCSV); -} diff --git a/sysid/src/main/native/cpp/view/LogLoader.cpp b/sysid/src/main/native/cpp/view/LogLoader.cpp new file mode 100644 index 00000000000..fdaa3af11be --- /dev/null +++ b/sysid/src/main/native/cpp/view/LogLoader.cpp @@ -0,0 +1,208 @@ +// 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 "sysid/view/LogLoader.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace sysid; + +LogLoader::LogLoader(glass::Storage& storage, wpi::Logger& logger) {} + +LogLoader::~LogLoader() = default; + +void LogLoader::Display() { + if (ImGui::Button("Open data log file...")) { + m_opener = std::make_unique( + "Select Data Log", "", + std::vector{"DataLog Files", "*.wpilog"}); + } + + // Handle opening the file + if (m_opener && m_opener->ready(0)) { + if (!m_opener->result().empty()) { + m_filename = m_opener->result()[0]; + + std::error_code ec; + auto buf = wpi::MemoryBuffer::GetFile(m_filename, ec); + if (ec) { + ImGui::OpenPopup("Error"); + m_error = fmt::format("Could not open file: {}", ec.message()); + return; + } + + wpi::log::DataLogReader reader{std::move(buf)}; + if (!reader.IsValid()) { + ImGui::OpenPopup("Error"); + m_error = "Not a valid datalog file"; + return; + } + unload(); + m_reader = + std::make_unique(std::move(reader)); + m_entryTree.clear(); + } + m_opener.reset(); + } + + // Handle errors + ImGui::SetNextWindowSize(ImVec2(480.f, 0.0f)); + if (ImGui::BeginPopupModal("Error")) { + ImGui::PushTextWrapPos(0.0f); + ImGui::TextUnformatted(m_error.c_str()); + ImGui::PopTextWrapPos(); + if (ImGui::Button("Close")) { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + if (!m_reader) { + return; + } + + // Summary info + ImGui::TextUnformatted(fs::path{m_filename}.stem().string().c_str()); + ImGui::Text("%u records, %u entries%s", m_reader->GetNumRecords(), + m_reader->GetNumEntries(), + m_reader->IsDone() ? "" : " (working)"); + + if (!m_reader->IsDone()) { + return; + } + + bool refilter = ImGui::InputText("Filter", &m_filter); + + // Display tree of entries + if (m_entryTree.empty() || refilter) { + RebuildEntryTree(); + } + + ImGui::BeginTable( + "Entries", 2, + ImGuiTableFlags_Borders | ImGuiTableFlags_SizingStretchProp); + ImGui::TableSetupColumn("Name"); + ImGui::TableSetupColumn("Type"); + // ImGui::TableSetupColumn("Metadata"); + ImGui::TableHeadersRow(); + DisplayEntryTree(m_entryTree); + ImGui::EndTable(); +} + +void LogLoader::RebuildEntryTree() { + m_entryTree.clear(); + wpi::SmallVector parts; + m_reader->ForEachEntryName([&](const glass::DataLogReaderEntry& entry) { + // only show double/float/string entries (TODO: support struct/protobuf) + if (entry.type != "double" && entry.type != "float" && + entry.type != "string") { + return; + } + + // filter on name + if (!m_filter.empty() && !wpi::contains_lower(entry.name, m_filter)) { + return; + } + + parts.clear(); + // split on first : if one is present + auto [prefix, mainpart] = wpi::split(entry.name, ':'); + if (mainpart.empty() || wpi::contains(prefix, '/')) { + mainpart = entry.name; + } else { + parts.emplace_back(prefix); + } + wpi::split(mainpart, parts, '/', -1, false); + + // ignore a raw "/" key + if (parts.empty()) { + return; + } + + // get to leaf + auto nodes = &m_entryTree; + for (auto part : wpi::drop_back(std::span{parts.begin(), parts.end()})) { + auto it = + std::find_if(nodes->begin(), nodes->end(), + [&](const auto& node) { return node.name == part; }); + if (it == nodes->end()) { + nodes->emplace_back(part); + // path is from the beginning of the string to the end of the current + // part; this works because part is a reference to the internals of + // entry.name + nodes->back().path.assign( + entry.name.data(), part.data() + part.size() - entry.name.data()); + it = nodes->end() - 1; + } + nodes = &it->children; + } + + auto it = std::find_if(nodes->begin(), nodes->end(), [&](const auto& node) { + return node.name == parts.back(); + }); + if (it == nodes->end()) { + nodes->emplace_back(parts.back()); + // no need to set path, as it's identical to entry.name + it = nodes->end() - 1; + } + it->entry = &entry; + }); +} + +static void EmitEntry(const std::string& name, + const glass::DataLogReaderEntry& entry) { + ImGui::TableNextColumn(); + ImGui::Selectable(name.c_str()); + if (ImGui::BeginDragDropSource()) { + auto entryPtr = &entry; + ImGui::SetDragDropPayload( + entry.type == "string" ? "DataLogEntryString" : "DataLogEntry", + &entryPtr, + sizeof(entryPtr)); // NOLINT + ImGui::TextUnformatted(entry.name.data(), + entry.name.data() + entry.name.size()); + ImGui::EndDragDropSource(); + } + ImGui::TableNextColumn(); + ImGui::TextUnformatted(entry.type.data(), + entry.type.data() + entry.type.size()); +#if 0 + ImGui::TableNextColumn(); + ImGui::TextUnformatted(entry.metadata.data(), + entry.metadata.data() + entry.metadata.size()); +#endif +} + +void LogLoader::DisplayEntryTree(const std::vector& tree) { + for (auto&& node : tree) { + if (node.entry) { + EmitEntry(node.name, *node.entry); + } + + if (!node.children.empty()) { + ImGui::TableNextColumn(); + bool open = ImGui::TreeNodeEx(node.name.c_str(), + ImGuiTreeNodeFlags_SpanFullWidth); + ImGui::TableNextColumn(); +#if 0 + ImGui::TableNextColumn(); +#endif + if (open) { + DisplayEntryTree(node.children); + ImGui::TreePop(); + } + } + } +} diff --git a/sysid/src/main/native/cpp/view/Logger.cpp b/sysid/src/main/native/cpp/view/Logger.cpp deleted file mode 100644 index 5e7773dbd0f..00000000000 --- a/sysid/src/main/native/cpp/view/Logger.cpp +++ /dev/null @@ -1,222 +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 "sysid/view/Logger.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sysid/Util.h" -#include "sysid/analysis/AnalysisType.h" -#include "sysid/view/UILayout.h" - -using namespace sysid; - -Logger::Logger(glass::Storage& storage, wpi::Logger& logger) - : m_logger{logger}, m_ntSettings{"sysid", storage} { - wpi::gui::AddEarlyExecute([&] { m_ntSettings.Update(); }); - - m_ntSettings.EnableServerOption(false); -} - -void Logger::Display() { - // Get the current width of the window. This will be used to scale - // our UI elements. - float width = ImGui::GetContentRegionAvail().x; - - // Add team number input and apply button for NT connection. - m_ntSettings.Display(); - - // Reset and clear the internal manager state. - ImGui::SameLine(); - if (ImGui::Button("Reset Telemetry")) { - m_settings = TelemetryManager::Settings{}; - m_manager = std::make_unique(m_settings, m_logger); - m_settings.mechanism = analysis::FromName(kTypes[m_selectedType]); - } - - // Add NT connection indicator. - static ImVec4 kColorDisconnected{1.0f, 0.4f, 0.4f, 1.0f}; - static ImVec4 kColorConnected{0.2f, 1.0f, 0.2f, 1.0f}; - ImGui::SameLine(); - bool ntConnected = nt::NetworkTableInstance::GetDefault().IsConnected(); - ImGui::TextColored(ntConnected ? kColorConnected : kColorDisconnected, - ntConnected ? "NT Connected" : "NT Disconnected"); - - // Create a Section for project configuration - ImGui::Separator(); - ImGui::Spacing(); - ImGui::Text("Project Parameters"); - - // Add a dropdown for mechanism type. - ImGui::SetNextItemWidth(ImGui::GetFontSize() * kTextBoxWidthMultiple); - - if (ImGui::Combo("Mechanism", &m_selectedType, kTypes, - IM_ARRAYSIZE(kTypes))) { - m_settings.mechanism = analysis::FromName(kTypes[m_selectedType]); - } - - // Add Dropdown for Units - ImGui::SetNextItemWidth(ImGui::GetFontSize() * kTextBoxWidthMultiple); - if (ImGui::Combo("Unit Type", &m_selectedUnit, kUnits, - IM_ARRAYSIZE(kUnits))) { - m_settings.units = kUnits[m_selectedUnit]; - } - - sysid::CreateTooltip( - "This is the type of units that your gains will be in. For example, if " - "you want your flywheel gains in terms of radians, then use the radians " - "unit. On the other hand, if your drivetrain will use gains in meters, " - "choose meters."); - - // Rotational units have fixed Units per rotations - m_isRotationalUnits = - (m_settings.units == "Rotations" || m_settings.units == "Degrees" || - m_settings.units == "Radians"); - if (m_settings.units == "Degrees") { - m_settings.unitsPerRotation = 360.0; - } else if (m_settings.units == "Radians") { - m_settings.unitsPerRotation = 2 * std::numbers::pi; - } else if (m_settings.units == "Rotations") { - m_settings.unitsPerRotation = 1.0; - } - - // Units Per Rotations entry - ImGui::SetNextItemWidth(ImGui::GetFontSize() * kTextBoxWidthMultiple); - ImGui::InputDouble("Units Per Rotation", &m_settings.unitsPerRotation, 0.0f, - 0.0f, "%.4f", - m_isRotationalUnits ? ImGuiInputTextFlags_ReadOnly - : ImGuiInputTextFlags_None); - sysid::CreateTooltip( - "The logger assumes that the code will be sending recorded motor shaft " - "rotations over NetworkTables. This value will then be multiplied by the " - "units per rotation to get the measurement in the units you " - "specified.\n\nFor non-rotational units (e.g. meters), this value is " - "usually the wheel diameter times pi (should not include gearing)."); - // Create a section for voltage parameters. - ImGui::Separator(); - ImGui::Spacing(); - ImGui::Text("Voltage Parameters"); - - auto CreateVoltageParameters = [this](const char* text, double* data, - float min, float max) { - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 6); - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, - m_manager && m_manager->IsActive()); - float value = static_cast(*data); - if (ImGui::SliderFloat(text, &value, min, max, "%.2f")) { - *data = value; - } - ImGui::PopItemFlag(); - }; - - CreateVoltageParameters("Quasistatic Ramp Rate (V/s)", - &m_settings.quasistaticRampRate, 0.10f, 0.60f); - sysid::CreateTooltip( - "This is the rate at which the voltage will increase during the " - "quasistatic test."); - - CreateVoltageParameters("Dynamic Step Voltage (V)", &m_settings.stepVoltage, - 0.0f, 10.0f); - sysid::CreateTooltip( - "This is the voltage that will be applied for the " - "dynamic voltage (acceleration) tests."); - - // Create a section for tests. - ImGui::Separator(); - ImGui::Spacing(); - ImGui::Text("Tests"); - - auto CreateTest = [this, width](const char* text, const char* itext) { - // Display buttons if we have an NT connection. - if (nt::NetworkTableInstance::GetDefault().IsConnected()) { - // Create button to run tests. - if (ImGui::Button(text)) { - // Open the warning message. - ImGui::OpenPopup("Warning"); - m_manager->BeginTest(itext); - m_opened = text; - } - if (m_opened == text && ImGui::BeginPopupModal("Warning")) { - ImGui::TextWrapped("%s", m_popupText.c_str()); - if (ImGui::Button(m_manager->IsActive() ? "End Test" : "Close")) { - m_manager->EndTest(); - ImGui::CloseCurrentPopup(); - m_opened = ""; - } - ImGui::EndPopup(); - } - } else { - // Show disabled text when there is no connection. - ImGui::TextDisabled("%s", text); - } - - // Show whether the tests were run or not. - bool run = m_manager->HasRunTest(itext); - ImGui::SameLine(width * 0.7); - ImGui::Text(run ? "Run" : "Not Run"); - }; - - CreateTest("Quasistatic Forward", "slow-forward"); - CreateTest("Quasistatic Backward", "slow-backward"); - CreateTest("Dynamic Forward", "fast-forward"); - CreateTest("Dynamic Backward", "fast-backward"); - - m_manager->RegisterDisplayCallback( - [this](const auto& str) { m_popupText = str; }); - - // Display the path to where the JSON will be saved and a button to select the - // location. - ImGui::Separator(); - ImGui::Spacing(); - ImGui::Text("Save Location"); - if (ImGui::Button("Choose")) { - m_selector = std::make_unique("Select Folder"); - } - ImGui::SameLine(); - ImGui::InputText("##savelocation", &m_jsonLocation, - ImGuiInputTextFlags_ReadOnly); - - // Add button to save. - ImGui::SameLine(width * 0.9); - if (ImGui::Button("Save")) { - try { - m_manager->SaveJSON(m_jsonLocation); - } catch (const std::exception& e) { - ImGui::OpenPopup("Exception Caught!"); - m_exception = e.what(); - } - } - - // Handle exceptions. - if (ImGui::BeginPopupModal("Exception Caught!")) { - ImGui::Text("%s", m_exception.c_str()); - if (ImGui::Button("Close")) { - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } - - // Run periodic methods. - SelectDataFolder(); - m_ntSettings.Update(); - m_manager->Update(); -} - -void Logger::SelectDataFolder() { - // If the selector exists and is ready with a result, we can store it. - if (m_selector && m_selector->ready()) { - m_jsonLocation = m_selector->result(); - m_selector.reset(); - } -} diff --git a/sysid/src/main/native/include/sysid/Util.h b/sysid/src/main/native/include/sysid/Util.h index 38cf8b2e42f..500601c9710 100644 --- a/sysid/src/main/native/include/sysid/Util.h +++ b/sysid/src/main/native/include/sysid/Util.h @@ -34,7 +34,7 @@ #define STRINGIZE(s) #s namespace sysid { -static constexpr const char* kUnits[] = {"Meters", "Feet", "Inches", +inline constexpr const char* kUnits[] = {"Meters", "Feet", "Inches", "Radians", "Rotations", "Degrees"}; /** @@ -45,6 +45,14 @@ static constexpr const char* kUnits[] = {"Meters", "Feet", "Inches", */ void CreateTooltip(const char* text); +/** + * Displays an error tooltip beside the widget that this method is called after + * with the provided text. + * + * @param text The text to show in the error tooltip. + */ +void CreateErrorTooltip(const char* text); + /** * Utility function to launch an error popup if an exception is detected. * diff --git a/sysid/src/main/native/include/sysid/analysis/AnalysisManager.h b/sysid/src/main/native/include/sysid/analysis/AnalysisManager.h index 956b3b58be6..6dfe51cb358 100644 --- a/sysid/src/main/native/include/sysid/analysis/AnalysisManager.h +++ b/sysid/src/main/native/include/sysid/analysis/AnalysisManager.h @@ -17,6 +17,7 @@ #include #include +#include #include #include "sysid/analysis/AnalysisType.h" @@ -33,14 +34,16 @@ namespace sysid { */ class AnalysisManager { public: + // This contains data for each test (e.g. quasistatic-forward, + // quasistatic-backward, etc) indexed by test name + TestData m_data; /** * Represents settings for an instance of the analysis manager. This contains - * information about the feedback controller preset, loop type, motion + * information about the feedback controller preset, loop type, velocity * threshold, acceleration window size, LQR parameters, and the selected * dataset. */ struct Settings { - enum class DrivetrainDataset { kCombined = 0, kLeft = 1, kRight = 2 }; /** * The feedback controller preset used to calculate gains. */ @@ -57,9 +60,9 @@ class AnalysisManager { LQRParameters lqr{1, 1.5, 7}; /** - * The motion threshold (units/s) for trimming quasistatic test data. + * The velocity threshold (units/s) for trimming quasistatic test data. */ - double motionThreshold = 0.2; + double velocityThreshold = 0.2; /** * The window size for the median filter. @@ -71,39 +74,63 @@ class AnalysisManager { * zero indicates it needs to be set to the default. */ units::second_t stepTestDuration = 0_s; + }; + struct FeedforwardGain { /** - * The conversion factor of counts per revolution. + * The feedforward gain. */ - int cpr = 1440; + double gain = 1; /** - * The conversion factor of gearing. + * Descriptor attached to the feedforward gain. */ - double gearing = 1; + std::string descriptor = "Feedforward gain."; /** - * Whether or not the gains should be in the encoder's units (mainly for use - * in a smart motor controller). + * Whether the feedforward gain is valid. */ - bool convertGainsToEncTicks = false; + bool isValidGain = true; - DrivetrainDataset dataset = DrivetrainDataset::kCombined; + /** + * Error message attached to the feedforward gain. + */ + std::string errorMessage = "No error."; }; /** - * Stores feedforward. + * Stores feedforward gains. */ struct FeedforwardGains { /** - * Stores the Feedforward gains. + * Stores the raw OLSResult from analysis. + */ + OLSResult olsResult; + + /** + * The static gain Ks. + */ + FeedforwardGain Ks = {}; + + /** + * The velocity gain kV. + */ + FeedforwardGain Kv = {}; + + /** + * The acceleration gain kA. + */ + FeedforwardGain Ka = {}; + + /** + * The gravity gain Kg. */ - OLSResult ffGains; + FeedforwardGain Kg = {}; /** - * Stores the trackwidth for angular drivetrain tests. + * The offset (arm). */ - std::optional trackWidth; + FeedforwardGain offset = {}; }; /** @@ -133,7 +160,8 @@ class AnalysisManager { * The keys (which contain sysid data) that are in the JSON to analyze. */ static constexpr const char* kJsonDataKeys[] = { - "slow-forward", "slow-backward", "fast-forward", "fast-backward"}; + "quasistatic-forward", "quasistatic-reverse", "dynamic-forward", + "dynamic-reverse"}; /** * Concatenates a list of vectors. The contents of the source vectors are @@ -170,12 +198,11 @@ class AnalysisManager { * Constructs an instance of the analysis manager with the given path (to the * JSON) and analysis manager settings. * - * @param path The path to the JSON containing the sysid data. + * @param data The data from the SysId routine. * @param settings The settings for this instance of the analysis manager. * @param logger The logger instance to use for log data. */ - AnalysisManager(std::string_view path, Settings& settings, - wpi::Logger& logger); + AnalysisManager(TestData data, Settings& settings, wpi::Logger& logger); /** * Prepares data from the JSON and stores the output in Storage member @@ -197,16 +224,15 @@ class AnalysisManager { * @param ff The feedforward gains. * @return The calculated feedback gains. */ - FeedbackGains CalculateFeedback(std::vector ff); + FeedbackGains CalculateFeedback(const FeedforwardGain& Kv, + const FeedforwardGain& Ka); /** * Overrides the units in the JSON with the user-provided ones. * * @param unit The unit to output gains in. - * @param unitsPerRotation The conversion factor between rotations and the - * selected unit. */ - void OverrideUnits(std::string_view unit, double unitsPerRotation); + void OverrideUnits(std::string_view unit); /** * Resets the units back to those defined in the JSON. @@ -218,21 +244,14 @@ class AnalysisManager { * * @return The analysis type. */ - const AnalysisType& GetAnalysisType() const { return m_type; } + const AnalysisType& GetAnalysisType() const { return m_data.mechanismType; } /** * Returns the units of analysis. * * @return The units of analysis. */ - std::string_view GetUnit() const { return m_unit; } - - /** - * Returns the factor (a.k.a. units per rotation) for analysis. - * - * @return The factor (a.k.a. units per rotation) for analysis. - */ - double GetFactor() const { return m_factor; } + std::string_view GetUnit() const { return m_data.distanceUnit; } /** * Returns a reference to the iterator of the currently selected raw datset. @@ -241,9 +260,7 @@ class AnalysisManager { * * @return A reference to the raw internal data. */ - Storage& GetRawData() { - return m_rawDataset[static_cast(m_settings.dataset)]; - } + Storage& GetRawData() { return m_rawDataset; } /** * Returns a reference to the iterator of the currently selected filtered @@ -252,18 +269,14 @@ class AnalysisManager { * * @return A reference to the filtered internal data. */ - Storage& GetFilteredData() { - return m_filteredDataset[static_cast(m_settings.dataset)]; - } + Storage& GetFilteredData() { return m_filteredDataset; } /** * Returns the original dataset. * * @return The original (untouched) dataset */ - Storage& GetOriginalData() { - return m_originalDataset[static_cast(m_settings.dataset)]; - } + Storage& GetOriginalData() { return m_originalDataset; } /** * Returns the minimum duration of the Step Voltage Test of the currently @@ -314,22 +327,14 @@ class AnalysisManager { return m_startTimes; } - bool HasData() const { - return !m_originalDataset[static_cast( - Settings::DrivetrainDataset::kCombined)] - .empty(); - } + bool HasData() const { return !m_originalDataset.empty(); } private: wpi::Logger& m_logger; - // This is used to store the various datasets (i.e. Combined, Forward, - // Backward, etc.) - wpi::json m_json; - - std::array m_originalDataset; - std::array m_rawDataset; - std::array m_filteredDataset; + Storage m_originalDataset; + Storage m_rawDataset; + Storage m_filteredDataset; // Stores the various start times of the different tests. std::array m_startTimes; @@ -338,24 +343,11 @@ class AnalysisManager { // controller preset, LQR parameters, acceleration window size, etc. Settings& m_settings; - // Miscellaneous data from the JSON -- the analysis type, the units, and the - // units per rotation. - AnalysisType m_type; - std::string m_unit; - double m_factor; - units::second_t m_minStepTime{0}; units::second_t m_maxStepTime{std::numeric_limits::infinity()}; std::vector m_positionDelays; std::vector m_velocityDelays; - // Stores an optional track width if we are doing the drivetrain angular test. - std::optional m_trackWidth; - void PrepareGeneralData(); - - void PrepareAngularDrivetrainData(); - - void PrepareLinearDrivetrainData(); }; } // namespace sysid diff --git a/sysid/src/main/native/include/sysid/analysis/AnalysisType.h b/sysid/src/main/native/include/sysid/analysis/AnalysisType.h index 7feedb3f51a..5a30d7ca868 100644 --- a/sysid/src/main/native/include/sysid/analysis/AnalysisType.h +++ b/sysid/src/main/native/include/sysid/analysis/AnalysisType.h @@ -52,11 +52,9 @@ struct AnalysisType { }; namespace analysis { -constexpr AnalysisType kDrivetrain{3, 9, "Drivetrain"}; -constexpr AnalysisType kDrivetrainAngular{3, 9, "Drivetrain (Angular)"}; -constexpr AnalysisType kElevator{4, 4, "Elevator"}; -constexpr AnalysisType kArm{5, 4, "Arm"}; -constexpr AnalysisType kSimple{3, 4, "Simple"}; +inline constexpr AnalysisType kElevator{4, 4, "Elevator"}; +inline constexpr AnalysisType kArm{5, 4, "Arm"}; +inline constexpr AnalysisType kSimple{3, 4, "Simple"}; AnalysisType FromName(std::string_view name); } // namespace analysis diff --git a/sysid/src/main/native/include/sysid/analysis/FeedbackAnalysis.h b/sysid/src/main/native/include/sysid/analysis/FeedbackAnalysis.h index 51754a241d4..397aa8797b7 100644 --- a/sysid/src/main/native/include/sysid/analysis/FeedbackAnalysis.h +++ b/sysid/src/main/native/include/sysid/analysis/FeedbackAnalysis.h @@ -48,18 +48,14 @@ struct FeedbackGains { * Calculates position feedback gains for the given controller preset, LQR * controller gain parameters and feedforward gains. * - * @param preset The feedback controller preset. - * @param params The parameters for calculating optimal feedback - * gains. - * @param Kv Velocity feedforward gain. - * @param Ka Acceleration feedforward gain. - * @param encFactor The factor to convert the gains from output units to - * encoder units. This is usually encoder EPR * gearing - * * units per rotation. + * @param preset The feedback controller preset. + * @param params The parameters for calculating optimal feedback gains. + * @param Kv Velocity feedforward gain. + * @param Ka Acceleration feedforward gain. */ FeedbackGains CalculatePositionFeedbackGains( const FeedbackControllerPreset& preset, const LQRParameters& params, - double Kv, double Ka, double encFactor = 1.0); + double Kv, double Ka); /** * Calculates velocity feedback gains for the given controller preset, LQR diff --git a/sysid/src/main/native/include/sysid/analysis/FeedbackControllerPreset.h b/sysid/src/main/native/include/sysid/analysis/FeedbackControllerPreset.h index 4b13c6c38f0..d946c48f750 100644 --- a/sysid/src/main/native/include/sysid/analysis/FeedbackControllerPreset.h +++ b/sysid/src/main/native/include/sysid/analysis/FeedbackControllerPreset.h @@ -71,11 +71,11 @@ struct FeedbackControllerPreset { enum class FeedbackControllerLoopType { kPosition, kVelocity }; namespace presets { -constexpr FeedbackControllerPreset kDefault{1.0, 1.0, 20_ms, true, 0_s}; +inline constexpr FeedbackControllerPreset kDefault{1.0, 1.0, 20_ms, true, 0_s}; -constexpr FeedbackControllerPreset kWPILibNew{kDefault}; -constexpr FeedbackControllerPreset kWPILibOld{1.0 / 12.0, 1.0, 50_ms, false, - 0_s}; +inline constexpr FeedbackControllerPreset kWPILibNew{kDefault}; +inline constexpr FeedbackControllerPreset kWPILibOld{1.0 / 12.0, 1.0, 50_ms, + false, 0_s}; // Measurement delay from a moving average filter: // @@ -117,10 +117,10 @@ constexpr FeedbackControllerPreset kWPILibOld{1.0 / 12.0, 1.0, 50_ms, false, * * Total delay = 50 ms + 31.5 ms = 81.5 ms. */ -constexpr FeedbackControllerPreset kCTRECANCoder{1.0 / 12.0, 60.0, 1_ms, true, - 81.5_ms}; -constexpr FeedbackControllerPreset kCTREDefault{1023.0 / 12.0, 0.1, 1_ms, false, - 81.5_ms}; +inline constexpr FeedbackControllerPreset kCTRECANCoder{1.0 / 12.0, 60.0, 1_ms, + true, 81.5_ms}; +inline constexpr FeedbackControllerPreset kCTREDefault{1023.0 / 12.0, 0.1, 1_ms, + false, 81.5_ms}; /** * https://api.ctr-electronics.com/phoenixpro/release/cpp/classctre_1_1phoenixpro_1_1hardware_1_1core_1_1_core_c_a_ncoder.html#a718a1a214b58d3c4543e88e3cb51ade5 * @@ -129,7 +129,8 @@ constexpr FeedbackControllerPreset kCTREDefault{1023.0 / 12.0, 0.1, 1_ms, false, * Pro devices make use of Kalman filters default-tuned to lowest latency, which * in testing is roughly 1 millisecond */ -constexpr FeedbackControllerPreset kCTREProDefault{1.0, 1.0, 1_ms, true, 1_ms}; +inline constexpr FeedbackControllerPreset kCTREProDefault{1.0, 1.0, 1_ms, true, + 1_ms}; /** * https://github.com/wpilibsuite/sysid/issues/258#issuecomment-1010658237 @@ -138,8 +139,8 @@ constexpr FeedbackControllerPreset kCTREProDefault{1.0, 1.0, 1_ms, true, 1_ms}; * * Total delay = 8-tap moving average delay = (8 - 1) / 2 * 32 ms = 112 ms. */ -constexpr FeedbackControllerPreset kREVNEOBuiltIn{1.0 / 12.0, 60.0, 1_ms, false, - 112_ms}; +inline constexpr FeedbackControllerPreset kREVNEOBuiltIn{1.0 / 12.0, 60.0, 1_ms, + false, 112_ms}; /** * https://www.revrobotics.com/content/sw/max/sw-docs/cpp/classrev_1_1_c_a_n_encoder.html#a7e6ce792bc0c0558fb944771df572e6a @@ -150,15 +151,15 @@ constexpr FeedbackControllerPreset kREVNEOBuiltIn{1.0 / 12.0, 60.0, 1_ms, false, * * Total delay = 50 ms + 31.5 ms = 81.5 ms. */ -constexpr FeedbackControllerPreset kREVNonNEO{1.0 / 12.0, 60.0, 1_ms, false, - 81.5_ms}; +inline constexpr FeedbackControllerPreset kREVNonNEO{1.0 / 12.0, 60.0, 1_ms, + false, 81.5_ms}; /** * https://github.com/wpilibsuite/sysid/pull/138#issuecomment-841734229 * * Backward finite difference delay = 10 ms / 2 = 5 ms. */ -constexpr FeedbackControllerPreset kVenom{4096.0 / 12.0, 60.0, 1_ms, false, - 5_ms}; +inline constexpr FeedbackControllerPreset kVenom{4096.0 / 12.0, 60.0, 1_ms, + false, 5_ms}; } // namespace presets } // namespace sysid diff --git a/sysid/src/main/native/include/sysid/analysis/FilteringUtils.h b/sysid/src/main/native/include/sysid/analysis/FilteringUtils.h index 9ea993da1d3..76411158b42 100644 --- a/sysid/src/main/native/include/sysid/analysis/FilteringUtils.h +++ b/sysid/src/main/native/include/sysid/analysis/FilteringUtils.h @@ -43,7 +43,7 @@ class InvalidDataError : public std::exception { explicit InvalidDataError(std::string_view message) { m_message = fmt::format( "{}. Please verify that your units and data is reasonable and then " - "adjust your motion threshold, test duration, and/or window size to " + "adjust your velocity threshold, test duration, and/or window size to " "try to fix this issue.", message); } @@ -64,7 +64,7 @@ class NoQuasistaticDataError : public std::exception { public: const char* what() const noexcept override { return "Quasistatic test trimming removed all data. Please adjust your " - "motion threshold and double check " + "velocity threshold and double check " "your units and test data to make sure that the robot is reporting " "reasonable values."; } @@ -98,6 +98,9 @@ double GetNoiseFloor( const std::vector& data, int window, std::function accessorFunction); +double GetMaxSpeed(const std::vector& data, + std::function accessorFunction); + /** * Reduces noise in velocity data by applying a median filter. * diff --git a/sysid/src/main/native/include/sysid/analysis/JSONConverter.h b/sysid/src/main/native/include/sysid/analysis/JSONConverter.h deleted file mode 100644 index 7581d25fc16..00000000000 --- a/sysid/src/main/native/include/sysid/analysis/JSONConverter.h +++ /dev/null @@ -1,24 +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 -#include - -#include - -namespace sysid { -/** - * Converts a JSON from the old frc-characterization format to the new sysid - * format. - * - * @param path The path to the old JSON. - * @param logger The logger instance for log messages. - * @return The full file path of the newly saved JSON. - */ -std::string ConvertJSON(std::string_view path, wpi::Logger& logger); - -std::string ToCSV(std::string_view path, wpi::Logger& logger); -} // namespace sysid diff --git a/sysid/src/main/native/include/sysid/analysis/Storage.h b/sysid/src/main/native/include/sysid/analysis/Storage.h index 52899a0096e..dad38e7cb54 100644 --- a/sysid/src/main/native/include/sysid/analysis/Storage.h +++ b/sysid/src/main/native/include/sysid/analysis/Storage.h @@ -4,12 +4,46 @@ #pragma once +#include #include #include +#include +#include + +#include "sysid/analysis/AnalysisType.h" namespace sysid { +struct MotorData { + // name of the *motor*, not the test + std::string name; + + // Data for a single contiguous motor test + // Timestamps are not necessarily aligned! + struct Run { + template + requires std::is_arithmetic_v || units::traits::is_unit_t_v + struct Sample { + Sample(units::second_t time, T measurement) + : time{time}, measurement{measurement} {} + units::second_t time; + T measurement; + }; + std::vector> voltage; + std::vector> position; + std::vector> velocity; + }; + + std::vector runs; +}; + +struct TestData { + std::string distanceUnit; + AnalysisType mechanismType; + wpi::StringMap motorData; +}; + /** * Represents each data point after it is cleaned and various parameters are * calculated. diff --git a/sysid/src/main/native/include/sysid/telemetry/TelemetryManager.h b/sysid/src/main/native/include/sysid/telemetry/TelemetryManager.h deleted file mode 100644 index 85ee09e520b..00000000000 --- a/sysid/src/main/native/include/sysid/telemetry/TelemetryManager.h +++ /dev/null @@ -1,237 +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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sysid/analysis/AnalysisType.h" - -namespace sysid { -/** - * This class is responsible for collecting data from the robot and storing it - * inside a JSON. - */ -class TelemetryManager { - public: - /** - * Represents settings for an instance of the TelemetryManager class. This - * contains information about the quasistatic ramp rate for slow tests, the - * step voltage for fast tests, and the mechanism type for characterization. - */ - struct Settings { - /** - * The rate at which the voltage should increase during the quasistatic test - * (V/s). - */ - double quasistaticRampRate = 0.25; - - /** - * The voltage that the dynamic test should run at (V). - */ - double stepVoltage = 7.0; - - /** - * The units the mechanism moves per recorded rotation. The sysid project - * will be recording things in rotations of the shaft so the - * unitsPerRotation is to convert those measurements to the units the user - * wants to use. - */ - double unitsPerRotation = 1.0; - - /** - * The name of the units used. - * Valid units: "Meters", "Feet", "Inches", "Radians", "Degrees", - * "Rotations" - */ - std::string units = "Meters"; - - /** - * The type of mechanism that will be analyzed. - * Supported mechanisms: Drivetrain, Angular Drivetrain, Elevator, Arm, - * Simple motor. - */ - AnalysisType mechanism = analysis::kDrivetrain; - }; - - /** - * Constructs an instance of the telemetry manager with the provided settings - * and NT instance to collect data over. - * - * @param settings The settings for this instance of the telemetry manager. - * @param logger The logger instance to use for log data. - * @param instance The NT instance to collect data over. The default value of - * this parameter should suffice in production; it should only - * be changed during unit testing. - */ - explicit TelemetryManager(const Settings& settings, wpi::Logger& logger, - nt::NetworkTableInstance instance = - nt::NetworkTableInstance::GetDefault()); - - /** - * Begins a test with the given parameters. - * - * @param name The name of the test. - */ - void BeginTest(std::string_view name); - - /** - * Ends the currently running test. If there is no test running, this is a - * no-op. - */ - void EndTest(); - - /** - * Updates the telemetry manager -- this adds a new autospeed entry and - * collects newest data from the robot. This must be called periodically by - * the user. - */ - void Update(); - - /** - * Registers a callback that's called by the TelemetryManager when there is a - * message to display to the user. - * - * @param callback Callback function that runs based off of the message - */ - void RegisterDisplayCallback(std::function callback) { - m_callbacks.emplace_back(std::move(callback)); - } - - /** - * Saves a JSON with the stored data at the given location. - * - * @param location The location to save the JSON at (this is the folder that - * should contain the saved JSON). - * @return The full file path of the saved JSON. - */ - std::string SaveJSON(std::string_view location); - - /** - * Returns whether a test is currently running. - * - * @return Whether a test is currently running. - */ - bool IsActive() const { return m_isRunningTest; } - - /** - * Returns whether the specified test is running or has run. - * - * @param name The test to check. - * - * @return Whether the specified test is running or has run. - */ - bool HasRunTest(std::string_view name) const { - return std::find(m_tests.cbegin(), m_tests.cend(), name) != m_tests.end(); - } - - /** - * Gets the size of the stored data. - * - * @return The size of the stored data - */ - size_t GetCurrentDataSize() const { return m_params.data.size(); } - - private: - enum class State { WaitingForEnable, RunningTest, WaitingForData }; - - /** - * Stores information about a currently running test. This information - * includes whether the robot will be traveling quickly (dynamic) or slowly - * (quasistatic), the direction of movement, the start time of the test, - * whether the robot is enabled, the current speed of the robot, and the - * collected data. - */ - struct TestParameters { - bool fast = false; - bool forward = false; - bool rotate = false; - - State state = State::WaitingForEnable; - - double enableStart = 0.0; - double disableStart = 0.0; - - bool enabled = false; - double speed = 0.0; - - std::string raw; - std::vector> data{}; - bool overflow = false; - bool mechError = false; - - TestParameters() = default; - TestParameters(bool fast, bool forward, bool rotate, State state) - : fast{fast}, forward{forward}, rotate{rotate}, state{state} {} - }; - - // Settings for this instance. - const Settings& m_settings; - - // Logger. - wpi::Logger& m_logger; - - // Test parameters for the currently running test. - TestParameters m_params; - bool m_isRunningTest = false; - - // A list of running or already run tests. - std::vector m_tests; - - // Stores the test data. - wpi::json m_data; - - // Display callbacks. - wpi::SmallVector, 1> m_callbacks; - - // NetworkTables instance and entries. - nt::NetworkTableInstance m_inst; - std::shared_ptr table = m_inst.GetTable("SmartDashboard"); - nt::DoublePublisher m_voltageCommand = - table->GetDoubleTopic("SysIdVoltageCommand").Publish(); - nt::StringPublisher m_testType = - table->GetStringTopic("SysIdTestType").Publish(); - nt::BooleanPublisher m_rotate = - table->GetBooleanTopic("SysIdRotate").Publish(); - nt::StringPublisher m_mechanism = - table->GetStringTopic("SysIdTest").Publish(); - nt::BooleanPublisher m_overflowPub = - table->GetBooleanTopic("SysIdOverflow").Publish(); - nt::BooleanSubscriber m_overflowSub = - table->GetBooleanTopic("SysIdOverflow").Subscribe(false); - nt::BooleanPublisher m_mechErrorPub = - table->GetBooleanTopic("SysIdWrongMech").Publish(); - nt::BooleanSubscriber m_mechErrorSub = - table->GetBooleanTopic("SysIdWrongMech").Subscribe(false); - nt::StringSubscriber m_telemetry = - table->GetStringTopic("SysIdTelemetry").Subscribe(""); - nt::IntegerSubscriber m_fmsControlData = - m_inst.GetTable("FMSInfo") - ->GetIntegerTopic("FMSControlData") - .Subscribe(0); - nt::DoublePublisher m_ackNumberPub = - table->GetDoubleTopic("SysIdAckNumber").Publish(); - nt::DoubleSubscriber m_ackNumberSub = - table->GetDoubleTopic("SysIdAckNumber").Subscribe(0); - - int m_ackNumber; -}; -} // namespace sysid diff --git a/sysid/src/main/native/include/sysid/view/Analyzer.h b/sysid/src/main/native/include/sysid/view/Analyzer.h index 2f30f610000..302bf983fe6 100644 --- a/sysid/src/main/native/include/sysid/view/Analyzer.h +++ b/sysid/src/main/native/include/sysid/view/Analyzer.h @@ -40,13 +40,14 @@ namespace sysid { */ class Analyzer : public glass::View { public: + TestData m_data; /** * The different display and processing states for the GUI */ enum class AnalyzerState { - kWaitingForJSON, + kWaitingForData, kNominalDisplay, - kMotionThresholdError, + kVelocityThresholdError, kTestDurationError, kGeneralDataError, kFileError @@ -90,12 +91,12 @@ class Analyzer : public glass::View { ~Analyzer() override { AbortDataPrep(); }; - private: /** - * Handles the logic for selecting a json to analyze + * Analyzes the selected data. */ - void SelectFile(); + void AnalyzeData(); + private: /** * Kills the data preparation thread */ @@ -112,11 +113,6 @@ class Analyzer : public glass::View { */ void DisplayGraphs(); - /** - * Displays the file selection widget. - */ - void DisplayFileSelector(); - /** * Resets the current analysis data. */ @@ -186,9 +182,17 @@ class Analyzer : public glass::View { void UpdateFeedbackGains(); /** - * Handles logic of displaying a gain on ImGui + * Handles logic of displaying a double on ImGui. */ - bool DisplayGain(const char* text, double* data, bool readOnly); + bool DisplayDouble(const char* text, double* data, bool readOnly); + + /** + * Displays a Feedforward gain, including the gain itself along with its + * validity and message. + */ + void DisplayFeedforwardGain(const char* text, + AnalysisManager::FeedforwardGain& ffGain, + bool readOnly); /** * Handles errors when they pop up. @@ -196,7 +200,7 @@ class Analyzer : public glass::View { void HandleError(std::string_view msg); // State of the Display GUI - AnalyzerState m_state = AnalyzerState::kWaitingForJSON; + AnalyzerState m_state = AnalyzerState::kWaitingForData; // Stores the exception message. std::string m_exception; @@ -214,36 +218,26 @@ class Analyzer : public glass::View { int m_selectedPreset = 0; // Feedforward and feedback gains. - std::vector m_ff; + AnalysisManager::FeedforwardGains m_feedforwardGains; double m_accelRSquared; double m_accelRMSE; double m_Kp; double m_Kd; units::millisecond_t m_timescale; - - // Track width - std::optional m_trackWidth; + bool m_timescaleValid = false; // Units int m_selectedOverrideUnit = 0; - double m_conversionFactor = 0.0; // Data analysis std::unique_ptr m_manager; int m_dataset = 0; int m_window = 8; double m_threshold = 0.2; - float m_stepTestDuration = 0.0; - - double m_gearingNumerator = 1.0; - double m_gearingDenominator = 1.0; + float m_stepTestDuration = 0; bool combinedGraphFit = false; - // File manipulation - std::unique_ptr m_selector; - std::string m_location; - // Logger wpi::Logger& m_logger; diff --git a/sysid/src/main/native/include/sysid/view/AnalyzerPlot.h b/sysid/src/main/native/include/sysid/view/AnalyzerPlot.h index e5fbe8ca959..2c234ad7b8e 100644 --- a/sysid/src/main/native/include/sysid/view/AnalyzerPlot.h +++ b/sysid/src/main/native/include/sysid/view/AnalyzerPlot.h @@ -17,6 +17,7 @@ #include #include +#include "sysid/analysis/AnalysisManager.h" #include "sysid/analysis/AnalysisType.h" #include "sysid/analysis/Storage.h" @@ -44,15 +45,16 @@ class AnalyzerPlot { * @param rawData Raw data storage. * @param filteredData Filtered data storage. * @param unit Unit of the dataset - * @param ff List of feedforward gains (Ks, Kv, Ka, and optionally - * Kg). + * @param ff Feedforward gains (Ks, Kv, Ka, optionally + * Kg and offset). * @param startTimes Array of dataset start times. * @param type Type of analysis. * @param abort Aborts analysis early if set to true from another * thread. */ void SetData(const Storage& rawData, const Storage& filteredData, - std::string_view unit, const std::vector& ff, + std::string_view unit, + const AnalysisManager::FeedforwardGains& ff, const std::array& startTimes, AnalysisType type, std::atomic& abort); diff --git a/sysid/src/main/native/include/sysid/view/DataSelector.h b/sysid/src/main/native/include/sysid/view/DataSelector.h new file mode 100644 index 00000000000..71732a7ed74 --- /dev/null +++ b/sysid/src/main/native/include/sysid/view/DataSelector.h @@ -0,0 +1,81 @@ +// 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sysid/analysis/Storage.h" + +namespace glass { +class DataLogReaderEntry; +class Storage; +} // namespace glass + +namespace wpi { +class Logger; +} // namespace wpi + +namespace sysid { +/** + * Helps with loading datalog files. + */ +class DataSelector : public glass::View { + public: + /** + * Creates a data selector widget + * + * @param logger The program logger + */ + explicit DataSelector(glass::Storage& storage, wpi::Logger& logger) + : m_logger{logger} {} + + /** + * Displays the log loader window. + */ + void Display() override; + + /** + * Resets view. Must be called whenever the DataLogReader goes away, as this + * class keeps references to DataLogReaderEntry objects. + */ + void Reset(); + + /** + * Called when new test data is loaded. + */ + std::function testdata; + + private: + wpi::Logger& m_logger; + using Runs = std::vector>; + using State = std::map>; // full name + using Tests = std::map>; // e.g. "dynamic" + std::future m_testsFuture; + Tests m_tests; + std::string m_selectedTest; + const glass::DataLogReaderEntry* m_testStateEntry = nullptr; + const glass::DataLogReaderEntry* m_velocityEntry = nullptr; + const glass::DataLogReaderEntry* m_positionEntry = nullptr; + const glass::DataLogReaderEntry* m_voltageEntry = nullptr; + double m_velocityScale = 1.0; + double m_positionScale = 1.0; + int m_selectedUnit = 0; + int m_selectedAnalysis = 0; + std::future m_testdataFuture; + std::vector m_testdataStats; + + static Tests LoadTests(const glass::DataLogReaderEntry& testStateEntry); + TestData BuildTestData(); +}; +} // namespace sysid diff --git a/sysid/src/main/native/include/sysid/view/JSONConverter.h b/sysid/src/main/native/include/sysid/view/JSONConverter.h deleted file mode 100644 index 89bfa3290d1..00000000000 --- a/sysid/src/main/native/include/sysid/view/JSONConverter.h +++ /dev/null @@ -1,57 +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 -#include -#include -#include - -#include -#include -#include - -namespace sysid { -/** - * Helps with converting different JSONs into different formats. Primarily - * enables users to convert an old 2020 FRC-Characterization JSON into a SysId - * JSON or a SysId JSON into a CSV file. - */ -class JSONConverter { - public: - /** - * Creates a JSONConverter widget - * - * @param logger The program logger - */ - explicit JSONConverter(wpi::Logger& logger) : m_logger(logger) {} - - /** - * Function to display the SysId JSON to CSV converter. - */ - void DisplayCSVConvert(); - - private: - /** - * Helper method to display a specific JSON converter - * - * @param tooltip The tooltip describing the JSON converter - * @param converter The function that takes a filename path and performs the - * previously specifid JSON conversion. - */ - void DisplayConverter( - const char* tooltip, - std::function converter); - - wpi::Logger& m_logger; - - std::string m_location; - std::unique_ptr m_opener; - - std::string m_exception; - - double m_timestamp = 0; -}; -} // namespace sysid diff --git a/sysid/src/main/native/include/sysid/view/LogLoader.h b/sysid/src/main/native/include/sysid/view/LogLoader.h new file mode 100644 index 00000000000..04ddd84dab3 --- /dev/null +++ b/sysid/src/main/native/include/sysid/view/LogLoader.h @@ -0,0 +1,78 @@ +// 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 +#include +#include +#include + +#include +#include + +namespace glass { +class DataLogReaderEntry; +class DataLogReaderThread; +class Storage; +} // namespace glass + +namespace pfd { +class open_file; +} // namespace pfd + +namespace wpi { +class Logger; +} // namespace wpi + +namespace sysid { +/** + * Helps with loading datalog files. + */ +class LogLoader : public glass::View { + public: + /** + * Creates a log loader widget + * + * @param logger The program logger + */ + explicit LogLoader(glass::Storage& storage, wpi::Logger& logger); + + ~LogLoader() override; + + /** + * Displays the log loader window. + */ + void Display() override; + + /** + * Signal called when the current file is unloaded (invalidates any + * LogEntry*). + */ + wpi::sig::Signal<> unload; + + private: + // wpi::Logger& m_logger; + + std::string m_filename; + std::unique_ptr m_opener; + std::unique_ptr m_reader; + + std::string m_error; + + std::string m_filter; + + struct EntryTreeNode { + explicit EntryTreeNode(std::string_view name) : name{name} {} + std::string name; // name of just this node + std::string path; // full path if entry is nullptr + const glass::DataLogReaderEntry* entry = nullptr; + std::vector children; // children, sorted by name + }; + std::vector m_entryTree; + + void RebuildEntryTree(); + void DisplayEntryTree(const std::vector& nodes); +}; +} // namespace sysid diff --git a/sysid/src/main/native/include/sysid/view/Logger.h b/sysid/src/main/native/include/sysid/view/Logger.h deleted file mode 100644 index d06d6508165..00000000000 --- a/sysid/src/main/native/include/sysid/view/Logger.h +++ /dev/null @@ -1,82 +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 -#include - -#include -#include -#include -#include -#include - -#include "sysid/telemetry/TelemetryManager.h" - -namespace glass { -class Storage; -} // namespace glass - -namespace sysid { -/** - * The logger GUI takes care of running the system idenfitication tests over - * NetworkTables and logging the data. This data is then stored in a JSON file - * which can be used for analysis. - */ -class Logger : public glass::View { - public: - /** - * Makes a logger widget. - * - * @param storage The glass storage object - * @param logger A logger object that keeps track of the program's logs - */ - Logger(glass::Storage& storage, wpi::Logger& logger); - - /** - * Displays the logger widget. - */ - void Display() override; - - /** - * The different mechanism / analysis types that are supported. - */ - static constexpr const char* kTypes[] = {"Drivetrain", "Drivetrain (Angular)", - "Arm", "Elevator", "Simple"}; - - /** - * The different units that are supported. - */ - static constexpr const char* kUnits[] = {"Meters", "Feet", "Inches", - "Radians", "Rotations", "Degrees"}; - - private: - /** - * Handles the logic of selecting a folder to save the SysId JSON to - */ - void SelectDataFolder(); - - wpi::Logger& m_logger; - - TelemetryManager::Settings m_settings; - int m_selectedType = 0; - int m_selectedUnit = 0; - - std::unique_ptr m_manager = - std::make_unique(m_settings, m_logger); - - std::unique_ptr m_selector; - std::string m_jsonLocation; - - glass::NetworkTablesSettings m_ntSettings; - - bool m_isRotationalUnits = false; - - std::string m_popupText; - - std::string m_opened; - std::string m_exception; -}; -} // namespace sysid diff --git a/sysid/src/main/native/include/sysid/view/UILayout.h b/sysid/src/main/native/include/sysid/view/UILayout.h index 732a1aaf658..f5d1e440f8f 100644 --- a/sysid/src/main/native/include/sysid/view/UILayout.h +++ b/sysid/src/main/native/include/sysid/view/UILayout.h @@ -62,9 +62,12 @@ inline constexpr Vector2d kLeftColSize{ 310, kAppWindowSize.y - kLeftColPos.y - kWindowGap}; // Left column contents -inline constexpr Vector2d kLoggerWindowPos = kLeftColPos; -inline constexpr Vector2d kLoggerWindowSize{ - kLeftColSize.x, kAppWindowSize.y - kWindowGap - kLoggerWindowPos.y}; +inline constexpr Vector2d kLogLoaderWindowPos = kLeftColPos; +inline constexpr Vector2d kLogLoaderWindowSize{kLeftColSize.x, 450}; +inline constexpr Vector2d kDataSelectorWindowPos = + kLogLoaderWindowPos + Vector2d{0, kLogLoaderWindowSize.y + kWindowGap}; +inline constexpr Vector2d kDataSelectorWindowSize{ + kLeftColSize.x, kAppWindowSize.y - kWindowGap - kDataSelectorWindowPos.y}; // Center column position and size inline constexpr Vector2d kCenterColPos = diff --git a/sysid/src/test/native/cpp/analysis/AnalysisTypeTest.cpp b/sysid/src/test/native/cpp/analysis/AnalysisTypeTest.cpp index 0abb2a1e9cc..51d348ccf37 100644 --- a/sysid/src/test/native/cpp/analysis/AnalysisTypeTest.cpp +++ b/sysid/src/test/native/cpp/analysis/AnalysisTypeTest.cpp @@ -7,10 +7,6 @@ #include "sysid/analysis/AnalysisType.h" TEST(AnalysisTypeTest, FromName) { - EXPECT_EQ(sysid::analysis::kDrivetrain, - sysid::analysis::FromName("Drivetrain")); - EXPECT_EQ(sysid::analysis::kDrivetrainAngular, - sysid::analysis::FromName("Drivetrain (Angular)")); EXPECT_EQ(sysid::analysis::kElevator, sysid::analysis::FromName("Elevator")); EXPECT_EQ(sysid::analysis::kArm, sysid::analysis::FromName("Arm")); EXPECT_EQ(sysid::analysis::kSimple, sysid::analysis::FromName("Simple")); diff --git a/sysid/src/test/native/cpp/analysis/FeedforwardAnalysisTest.cpp b/sysid/src/test/native/cpp/analysis/FeedforwardAnalysisTest.cpp index 7a087d85b73..d8cd79eda8d 100644 --- a/sysid/src/test/native/cpp/analysis/FeedforwardAnalysisTest.cpp +++ b/sysid/src/test/native/cpp/analysis/FeedforwardAnalysisTest.cpp @@ -28,7 +28,7 @@ enum Movements : uint32_t { kFastBackward }; -constexpr int kMovementCombinations = 16; +inline constexpr int kMovementCombinations = 16; /** * Return simulated test data for a given simulation model. @@ -241,54 +241,6 @@ TEST(FeedforwardAnalysisTest, Arm) { } } -TEST(FeedforwardAnalysisTest, Drivetrain) { - { - constexpr double Ks = 1.01; - constexpr double Kv = 3.060; - constexpr double Ka = 0.327; - - sysid::SimpleMotorSim model{Ks, Kv, Ka}; - - RunTests(model, sysid::analysis::kDrivetrain, {{Ks, Kv, Ka}}, - {{8e-3, 8e-3, 8e-3}}); - } - - { - constexpr double Ks = 0.547; - constexpr double Kv = 0.0693; - constexpr double Ka = 0.1170; - - sysid::SimpleMotorSim model{Ks, Kv, Ka}; - - RunTests(model, sysid::analysis::kDrivetrain, {{Ks, Kv, Ka}}, - {{8e-3, 8e-3, 8e-3}}); - } -} - -TEST(FeedforwardAnalysisTest, DrivetrainAngular) { - { - constexpr double Ks = 1.01; - constexpr double Kv = 3.060; - constexpr double Ka = 0.327; - - sysid::SimpleMotorSim model{Ks, Kv, Ka}; - - RunTests(model, sysid::analysis::kDrivetrainAngular, {{Ks, Kv, Ka}}, - {{8e-3, 8e-3, 8e-3}}); - } - - { - constexpr double Ks = 0.547; - constexpr double Kv = 0.0693; - constexpr double Ka = 0.1170; - - sysid::SimpleMotorSim model{Ks, Kv, Ka}; - - RunTests(model, sysid::analysis::kDrivetrainAngular, {{Ks, Kv, Ka}}, - {{8e-3, 8e-3, 8e-3}}); - } -} - TEST(FeedforwardAnalysisTest, Elevator) { { constexpr double Ks = 1.01; diff --git a/sysid/src/test/native/cpp/analysis/FilterTest.cpp b/sysid/src/test/native/cpp/analysis/FilterTest.cpp index a7b03492747..099bebc98a1 100644 --- a/sysid/src/test/native/cpp/analysis/FilterTest.cpp +++ b/sysid/src/test/native/cpp/analysis/FilterTest.cpp @@ -43,33 +43,96 @@ TEST(FilterTest, NoiseFloor) { EXPECT_NEAR(0.953, noiseFloor, 0.001); } +void FillStepVoltageData(std::vector& data) { + auto previousDatum = data.front(); + for (size_t i = 1; i < data.size(); ++i) { + auto& datum = data.at(i); + datum.timestamp = previousDatum.timestamp + previousDatum.dt; + datum.position = 0.5 * previousDatum.acceleration * + units::math::pow<2>(previousDatum.dt).value() + + previousDatum.velocity * previousDatum.dt.value() + + previousDatum.position; + datum.velocity = previousDatum.velocity + + previousDatum.acceleration * previousDatum.dt.value(); + + previousDatum = datum; + } +} + TEST(FilterTest, StepTrim) { - std::vector testData = { - {0_s, 1, 2, 3, 5_ms, 0, 0}, {1_s, 1, 2, 3, 5_ms, 0.25, 0}, - {2_s, 1, 2, 3, 5_ms, 0.5, 0}, {3_s, 1, 2, 3, 5_ms, 0.45, 0}, - {4_s, 1, 2, 3, 5_ms, 0.35, 0}, {5_s, 1, 2, 3, 5_ms, 0.15, 0}, - {6_s, 1, 2, 3, 5_ms, 0, 0}, {7_s, 1, 2, 3, 5_ms, 0.02, 0}, - {8_s, 1, 2, 3, 5_ms, 0.01, 0}, {9_s, 1, 2, 3, 5_ms, 0, 0}, - }; - - std::vector expectedData = { - {2_s, 1, 2, 3, 5_ms, 0.5, 0}, - {3_s, 1, 2, 3, 5_ms, 0.45, 0}, - {4_s, 1, 2, 3, 5_ms, 0.35, 0}, - {5_s, 1, 2, 3, 5_ms, 0.15, 0}}; - - auto maxTime = 9_s; - auto minTime = maxTime; - - sysid::AnalysisManager::Settings settings; - auto [tempMinTime, positionDelay, velocityDelay] = - sysid::TrimStepVoltageData(&testData, &settings, minTime, maxTime); - minTime = tempMinTime; - - EXPECT_EQ(expectedData[0].acceleration, testData[0].acceleration); - EXPECT_EQ(expectedData.back().acceleration, testData.back().acceleration); - EXPECT_EQ(5, settings.stepTestDuration.value()); - EXPECT_EQ(2, minTime.value()); + { + std::vector forwardTestData = { + {0_s, 1, 0, 0, 1_s, 0}, {0_s, 1, 0, 0, 1_s, 0.25}, + {0_s, 1, 0, 0, 1_s, 10}, {0_s, 1, 0, 0, 1_s, 0.45}, + {0_s, 1, 0, 0, 1_s, 0}, {0_s, 1, 0, 0, 1_s, 0.15}, + {0_s, 1, 0, 0, 1_s, 0}, {0_s, 1, 0, 0, 1_s, 0.02}, + {0_s, 1, 0, 0, 1_s, 0}, {0_s, 1, 0, 0, 0_s, 0}, + }; + + FillStepVoltageData(forwardTestData); + + auto maxTime = 9_s; + auto minTime = maxTime; + + sysid::AnalysisManager::Settings settings; + auto [tempMinTime, positionDelay, velocityDelay] = + sysid::TrimStepVoltageData(&forwardTestData, &settings, minTime, + maxTime); + minTime = tempMinTime; + + EXPECT_EQ(3, settings.stepTestDuration.value()); + EXPECT_EQ(2, minTime.value()); + } + + { + std::vector backwardsTestData = { + {0_s, -1, 0, 0, 1_s, 0}, {0_s, -1, 0, 0, 1_s, -0.46}, + {0_s, -1, 0, 0, 1_s, -8}, {0_s, -1, 0, 0, 1_s, -0.32}, + {0_s, -1, 0, 0, 1_s, -0.12}, {0_s, -1, 0, 0, 1_s, -0.08}, + {0_s, -1, 0, 0, 1_s, -0.06}, {0_s, -1, 0, 0, 1_s, -0.02}, + {0_s, -1, 0, 0, 1_s, 0}, {0_s, -1, 0, 0, 0_s, 0}, + }; + + FillStepVoltageData(backwardsTestData); + + auto maxTime = 9_s; + auto minTime = maxTime; + + sysid::AnalysisManager::Settings settings; + auto [tempMinTime, positionDelay, velocityDelay] = + sysid::TrimStepVoltageData(&backwardsTestData, &settings, minTime, + maxTime); + minTime = tempMinTime; + + EXPECT_EQ(3, settings.stepTestDuration.value()); + EXPECT_EQ(2, minTime.value()); + } + + { + // Forward test but with an erroneous negative acceleration at the end + std::vector noisyTestData = { + {0_s, 1, 0, 0, 1_s, 0}, {0_s, 1, 0, 0, 1_s, 0.41}, + {0_s, 1, 0, 0, 1_s, 11.5}, {0_s, 1, 0, 0, 1_s, 1.2}, + {0_s, 1, 0, 0, 1_s, 0.34}, {0_s, 1, 0, 0, 1_s, 0.25}, + {0_s, 1, 0, 0, 1_s, 0.11}, {0_s, 1, 0, 0, 1_s, -0.08}, + {0_s, 1, 0, 0, 1_s, -12}, {0_s, 1, 0, 0, 0_s, 0}, + }; + + FillStepVoltageData(noisyTestData); + + auto maxTime = 9_s; + auto minTime = maxTime; + + sysid::AnalysisManager::Settings settings; + auto [tempMinTime, positionDelay, velocityDelay] = + sysid::TrimStepVoltageData(&noisyTestData, &settings, minTime, maxTime); + minTime = tempMinTime; + + // Expect trimming to reject the erroneous peak negative accel, + // correctly picking up the max positive accel instead. + EXPECT_EQ(4, settings.stepTestDuration.value()); + EXPECT_EQ(2, minTime.value()); + } } template diff --git a/sysid/src/test/native/cpp/analysis/OLSTest.cpp b/sysid/src/test/native/cpp/analysis/OLSTest.cpp index 558b7e35364..00d14b983cb 100644 --- a/sysid/src/test/native/cpp/analysis/OLSTest.cpp +++ b/sysid/src/test/native/cpp/analysis/OLSTest.cpp @@ -14,9 +14,9 @@ TEST(OLSTest, TwoVariablesTwoPoints) { auto [coeffs, rSquared, rmse] = sysid::OLS(X, y); EXPECT_EQ(coeffs.size(), 2u); - EXPECT_NEAR(coeffs[0], 1.0, 0.05); - EXPECT_NEAR(coeffs[1], 2.0, 0.05); - EXPECT_NEAR(rSquared, 1.0, 1e-4); + EXPECT_DOUBLE_EQ(coeffs[0], 1.0); + EXPECT_DOUBLE_EQ(coeffs[1], 2.0); + EXPECT_DOUBLE_EQ(rSquared, 1.0); } TEST(OLSTest, TwoVariablesFivePoints) { @@ -28,9 +28,9 @@ TEST(OLSTest, TwoVariablesFivePoints) { auto [coeffs, rSquared, rmse] = sysid::OLS(X, y); EXPECT_EQ(coeffs.size(), 2u); - EXPECT_NEAR(coeffs[0], 0.305, 0.05); - EXPECT_NEAR(coeffs[1], 1.518, 0.05); - EXPECT_NEAR(rSquared, 0.985, 0.05); + EXPECT_DOUBLE_EQ(coeffs[0], 0.30487804878048774); + EXPECT_DOUBLE_EQ(coeffs[1], 1.5182926829268293); + EXPECT_DOUBLE_EQ(rSquared, 0.91906029466386019); } #ifndef NDEBUG diff --git a/upstream_utils/update_expected.py b/upstream_utils/update_expected.py new file mode 100755 index 00000000000..1b459fe5ac3 --- /dev/null +++ b/upstream_utils/update_expected.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +import os +import re +import shutil + +from upstream_utils import ( + get_repo_root, + clone_repo, + comment_out_invalid_includes, + walk_cwd_and_copy_if, + git_am, +) + + +def main(): + upstream_root = clone_repo( + "https://github.com/TartanLlama/expected", + # master on 2024-01-25 + "3f0ca7b19253129700a073abfa6d8638d9f7c80c", + shallow=False, + ) + wpilib_root = get_repo_root() + wpiutil = os.path.join(wpilib_root, "wpiutil") + + # Copy expected header into allwpilib + dest_filename = os.path.join( + wpiutil, "src/main/native/thirdparty/expected/include/wpi/expected" + ) + shutil.copyfile( + os.path.join(upstream_root, "include/tl/expected.hpp"), dest_filename + ) + + # Rename namespace from tl to wpi + with open(dest_filename) as f: + content = f.read() + content = content.replace("namespace tl", "namespace wpi") + content = content.replace("tl::", "wpi::") + content = content.replace("TL_", "WPI_") + with open(dest_filename, "w") as f: + f.write(content) + + +if __name__ == "__main__": + main() diff --git a/wpilibNewCommands/CMakeLists.txt b/wpilibNewCommands/CMakeLists.txt index 0671e467dd6..4b42aa56b82 100644 --- a/wpilibNewCommands/CMakeLists.txt +++ b/wpilibNewCommands/CMakeLists.txt @@ -71,18 +71,18 @@ target_include_directories( $ ) -install(TARGETS wpilibNewCommands EXPORT wpilibNewCommands) +install(TARGETS wpilibNewCommands EXPORT wpilibnewcommands) install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/wpilibNewCommands") configure_file( - wpilibNewCommands-config.cmake.in - ${WPILIB_BINARY_DIR}/wpilibNewCommands-config.cmake + wpilibnewcommands-config.cmake.in + ${WPILIB_BINARY_DIR}/wpilibnewcommands-config.cmake ) install( - FILES ${WPILIB_BINARY_DIR}/wpilibNewCommands-config.cmake + FILES ${WPILIB_BINARY_DIR}/wpilibnewcommands-config.cmake DESTINATION share/wpilibNewCommands ) -install(EXPORT wpilibNewCommands DESTINATION share/wpilibNewCommands) +install(EXPORT wpilibnewcommands DESTINATION share/wpilibNewCommands) if(WITH_TESTS) wpilib_add_test(wpilibNewCommands src/test/native/cpp) diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Command.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Command.java index f46feb1b2a0..5ea1a362ec5 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Command.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Command.java @@ -25,8 +25,10 @@ *

This class is provided by the NewCommands VendorDep */ public abstract class Command implements Sendable { + /** Requirements set. */ protected Set m_requirements = new HashSet<>(); + /** Default constructor. */ @SuppressWarnings("this-escape") protected Command() { String name = getClass().getName(); diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandBase.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandBase.java index 12243cb58b6..d75f08a29e0 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandBase.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandBase.java @@ -16,4 +16,9 @@ */ @Deprecated(since = "2024", forRemoval = true) @SuppressWarnings("PMD.AbstractClassWithoutAnyMethod") -public abstract class CommandBase extends Command {} +public abstract class CommandBase extends Command { + /** Default constructor. */ + public CommandBase() { + super(); + } +} diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/FunctionalCommand.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/FunctionalCommand.java index b685ef6b9d9..9f1d8a8130a 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/FunctionalCommand.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/FunctionalCommand.java @@ -18,10 +18,10 @@ *

This class is provided by the NewCommands VendorDep */ public class FunctionalCommand extends Command { - protected final Runnable m_onInit; - protected final Runnable m_onExecute; - protected final Consumer m_onEnd; - protected final BooleanSupplier m_isFinished; + private final Runnable m_onInit; + private final Runnable m_onExecute; + private final Consumer m_onEnd; + private final BooleanSupplier m_isFinished; /** * Creates a new FunctionalCommand. diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/NotifierCommand.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/NotifierCommand.java index b2d0938bd9e..4109091cd11 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/NotifierCommand.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/NotifierCommand.java @@ -18,8 +18,8 @@ *

This class is provided by the NewCommands VendorDep */ public class NotifierCommand extends Command { - protected final Notifier m_notifier; - protected final double m_period; + private final Notifier m_notifier; + private final double m_period; /** * Creates a new NotifierCommand. diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDCommand.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDCommand.java index c761f3f18df..26627fa30ff 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDCommand.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDCommand.java @@ -19,9 +19,16 @@ *

This class is provided by the NewCommands VendorDep */ public class PIDCommand extends Command { + /** PID controller. */ protected final PIDController m_controller; + + /** Measurement getter. */ protected DoubleSupplier m_measurement; + + /** Setpoint getter. */ protected DoubleSupplier m_setpoint; + + /** PID controller output consumer. */ protected DoubleConsumer m_useOutput; /** diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDSubsystem.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDSubsystem.java index e1c0dc893ec..8146ca4d7b0 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDSubsystem.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDSubsystem.java @@ -15,7 +15,10 @@ *

This class is provided by the NewCommands VendorDep */ public abstract class PIDSubsystem extends SubsystemBase { + /** PID controller. */ protected final PIDController m_controller; + + /** Whether PID controller output is enabled. */ protected boolean m_enabled; /** @@ -47,6 +50,11 @@ public void periodic() { } } + /** + * Returns the PIDController. + * + * @return The controller. + */ public PIDController getController() { return m_controller; } diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java index f7175fc2ebe..4e82811739b 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java @@ -21,9 +21,16 @@ *

This class is provided by the NewCommands VendorDep */ public class ProfiledPIDCommand extends Command { + /** Profiled PID controller. */ protected final ProfiledPIDController m_controller; + + /** Measurement getter. */ protected DoubleSupplier m_measurement; + + /** Goal getter. */ protected Supplier m_goal; + + /** Profiled PID controller output consumer. */ protected BiConsumer m_useOutput; /** diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDSubsystem.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDSubsystem.java index b8bb39843aa..05318edc611 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDSubsystem.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDSubsystem.java @@ -17,7 +17,10 @@ *

This class is provided by the NewCommands VendorDep */ public abstract class ProfiledPIDSubsystem extends SubsystemBase { + /** Profiled PID controller. */ protected final ProfiledPIDController m_controller; + + /** Whether the profiled PID controller output is enabled. */ protected boolean m_enabled; /** @@ -47,6 +50,11 @@ public void periodic() { } } + /** + * Returns the ProfiledPIDController. + * + * @return The controller. + */ public ProfiledPIDController getController() { return m_controller; } diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RepeatCommand.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RepeatCommand.java index 383cc9a647d..70ff63c6103 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RepeatCommand.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RepeatCommand.java @@ -20,7 +20,7 @@ *

This class is provided by the NewCommands VendorDep */ public class RepeatCommand extends Command { - protected final Command m_command; + private final Command m_command; private boolean m_ended; /** diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileSubsystem.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileSubsystem.java index 54d4edef5c3..44abdfc4db9 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileSubsystem.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileSubsystem.java @@ -63,7 +63,7 @@ public TrapezoidProfileSubsystem(TrapezoidProfile.Constraints constraints) { @Override public void periodic() { - m_state = m_profile.calculate(m_period, m_goal, m_state); + m_state = m_profile.calculate(m_period, m_state, m_goal); if (m_enabled) { useState(m_state); } diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WaitCommand.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WaitCommand.java index 828e8f5932b..6d4c52bb61c 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WaitCommand.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WaitCommand.java @@ -14,7 +14,9 @@ *

This class is provided by the NewCommands VendorDep */ public class WaitCommand extends Command { + /** The timer used for waiting. */ protected Timer m_timer = new Timer(); + private final double m_duration; /** diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WrapperCommand.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WrapperCommand.java index 5e1cb05a076..5e6ccaef82b 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WrapperCommand.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/WrapperCommand.java @@ -15,6 +15,7 @@ * subsystems its components require. */ public abstract class WrapperCommand extends Command { + /** Command being wrapped. */ protected final Command m_command; /** diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/InternalButton.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/InternalButton.java index f4897f2acac..87c9b739b3e 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/InternalButton.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/InternalButton.java @@ -42,10 +42,20 @@ private InternalButton(AtomicBoolean state, AtomicBoolean inverted) { this.m_inverted = inverted; } + /** + * Sets whether to invert button state. + * + * @param inverted Whether button state should be inverted. + */ public void setInverted(boolean inverted) { m_inverted.set(inverted); } + /** + * Sets whether button is pressed. + * + * @param pressed Whether button is pressed. + */ public void setPressed(boolean pressed) { m_pressed.set(pressed); } diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/sysid/SysIdRoutine.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/sysid/SysIdRoutine.java new file mode 100644 index 00000000000..053a404c779 --- /dev/null +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/sysid/SysIdRoutine.java @@ -0,0 +1,287 @@ +// 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.wpilibj2.command.sysid; + +import static edu.wpi.first.units.MutableMeasure.mutable; +import static edu.wpi.first.units.Units.Second; +import static edu.wpi.first.units.Units.Seconds; +import static edu.wpi.first.units.Units.Volts; +import static java.util.Map.entry; + +import edu.wpi.first.units.Measure; +import edu.wpi.first.units.MutableMeasure; +import edu.wpi.first.units.Time; +import edu.wpi.first.units.Velocity; +import edu.wpi.first.units.Voltage; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.first.wpilibj.sysid.SysIdRoutineLog; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Subsystem; +import java.util.Map; +import java.util.function.Consumer; + +/** + * A SysId characterization routine for a single mechanism. Mechanisms may have multiple motors. + * + *

A single subsystem may have multiple mechanisms, but mechanisms should not share test + * routines. Each complete test of a mechanism should have its own SysIdRoutine instance, since the + * log name of the recorded data is determined by the mechanism name. + * + *

The test state (e.g. "quasistatic-forward") is logged once per iteration during test + * execution, and once with state "none" when a test ends. Motor frames are logged every iteration + * during test execution. + * + *

Timestamps are not coordinated across data, so motor frames and test state tags may be + * recorded on different log frames. Because frame alignment is not guaranteed, SysId parses the log + * by using the test state flag to determine the timestamp range for each section of the test, and + * then extracts the motor frames within the valid timestamp ranges. If a given test was run + * multiple times in a single logfile, the user will need to select which of the tests to use for + * the fit in the analysis tool. + */ +public class SysIdRoutine extends SysIdRoutineLog { + private final Config m_config; + private final Mechanism m_mechanism; + private final MutableMeasure m_outputVolts = mutable(Volts.of(0)); + private final Consumer m_recordState; + + /** + * Create a new SysId characterization routine. + * + * @param config Hardware-independent parameters for the SysId routine. + * @param mechanism Hardware interface for the SysId routine. + */ + public SysIdRoutine(Config config, Mechanism mechanism) { + super(mechanism.m_name); + m_config = config; + m_mechanism = mechanism; + m_recordState = config.m_recordState != null ? config.m_recordState : this::recordState; + } + + /** Hardware-independent configuration for a SysId test routine. */ + public static class Config { + /** The voltage ramp rate used for quasistatic test routines. */ + public final Measure> m_rampRate; + + /** The step voltage output used for dynamic test routines. */ + public final Measure m_stepVoltage; + + /** Safety timeout for the test routine commands. */ + public final Measure

rampRate: 1 volt/sec + * + *

stepVoltage: 7 volts + * + *

timeout: 10 seconds + */ + public Config() { + this(null, null, null, null); + } + } + + /** + * A mechanism to be characterized by a SysId routine. Defines callbacks needed for the SysId test + * routine to control and record data from the mechanism. + */ + public static class Mechanism { + /** Sends the SysId-specified drive signal to the mechanism motors during test routines. */ + public final Consumer> m_drive; + + /** + * Returns measured data (voltages, positions, velocities) of the mechanism motors during test + * routines. + */ + public final Consumer m_log; + + /** The subsystem containing the motor(s) that is (or are) being characterized. */ + public final Subsystem m_subsystem; + + /** The name of the mechanism being tested. */ + public final String m_name; + + /** + * Create a new mechanism specification for a SysId routine. + * + * @param drive Sends the SysId-specified drive signal to the mechanism motors during test + * routines. + * @param log Returns measured data of the mechanism motors during test routines. To return + * data, call `motor(string motorName)` on the supplied `SysIdRoutineLog` instance, and then + * call one or more of the chainable logging handles (e.g. `voltage`) on the returned + * `MotorLog`. Multiple motors can be logged in a single callback by calling `motor` + * multiple times. + * @param subsystem The subsystem containing the motor(s) that is (or are) being characterized. + * Will be declared as a requirement for the returned test commands. + * @param name The name of the mechanism being tested. Will be appended to the log entry title + * for the routine's test state, e.g. "sysid-test-state-mechanism". Defaults to the name of + * the subsystem if left null. + */ + public Mechanism( + Consumer> drive, + Consumer log, + Subsystem subsystem, + String name) { + m_drive = drive; + m_log = log != null ? log : l -> {}; + m_subsystem = subsystem; + m_name = name != null ? name : subsystem.getName(); + } + + /** + * Create a new mechanism specification for a SysId routine. Defaults the mechanism name to the + * subsystem name. + * + * @param drive Sends the SysId-specified drive signal to the mechanism motors during test + * routines. + * @param log Returns measured data of the mechanism motors during test routines. To return + * data, call `motor(string motorName)` on the supplied `SysIdRoutineLog` instance, and then + * call one or more of the chainable logging handles (e.g. `voltage`) on the returned + * `MotorLog`. Multiple motors can be logged in a single callback by calling `motor` + * multiple times. + * @param subsystem The subsystem containing the motor(s) that is (or are) being characterized. + * Will be declared as a requirement for the returned test commands. The subsystem's `name` + * will be appended to the log entry title for the routine's test state, e.g. + * "sysid-test-state-subsystem". + */ + public Mechanism( + Consumer> drive, Consumer log, Subsystem subsystem) { + this(drive, log, subsystem, null); + } + } + + /** Motor direction for a SysId test. */ + public enum Direction { + /** Forward. */ + kForward, + /** Reverse. */ + kReverse + } + + /** + * Returns a command to run a quasistatic test in the specified direction. + * + *

The command will call the `drive` and `log` callbacks supplied at routine construction once + * per iteration. Upon command end or interruption, the `drive` callback is called with a value of + * 0 volts. + * + * @param direction The direction in which to run the test. + * @return A command to run the test. + */ + public Command quasistatic(Direction direction) { + State state; + if (direction == Direction.kForward) { + state = State.kQuasistaticForward; + } else { // if (direction == Direction.kReverse) { + state = State.kQuasistaticReverse; + } + + double outputSign = direction == Direction.kForward ? 1.0 : -1.0; + + Timer timer = new Timer(); + return m_mechanism + .m_subsystem + .runOnce(timer::restart) + .andThen( + m_mechanism.m_subsystem.run( + () -> { + m_mechanism.m_drive.accept( + m_outputVolts.mut_replace( + outputSign * timer.get() * m_config.m_rampRate.in(Volts.per(Second)), + Volts)); + m_mechanism.m_log.accept(this); + m_recordState.accept(state); + })) + .finallyDo( + () -> { + m_mechanism.m_drive.accept(Volts.of(0)); + m_recordState.accept(State.kNone); + timer.stop(); + }) + .withName("sysid-" + state.toString() + "-" + m_mechanism.m_name) + .withTimeout(m_config.m_timeout.in(Seconds)); + } + + /** + * Returns a command to run a dynamic test in the specified direction. + * + *

The command will call the `drive` and `log` callbacks supplied at routine construction once + * per iteration. Upon command end or interruption, the `drive` callback is called with a value of + * 0 volts. + * + * @param direction The direction in which to run the test. + * @return A command to run the test. + */ + public Command dynamic(Direction direction) { + double outputSign = direction == Direction.kForward ? 1.0 : -1.0; + State state = + Map.ofEntries( + entry(Direction.kForward, State.kDynamicForward), + entry(Direction.kReverse, State.kDynamicReverse)) + .get(direction); + + return m_mechanism + .m_subsystem + .runOnce( + () -> m_outputVolts.mut_replace(m_config.m_stepVoltage.in(Volts) * outputSign, Volts)) + .andThen( + m_mechanism.m_subsystem.run( + () -> { + m_mechanism.m_drive.accept(m_outputVolts); + m_mechanism.m_log.accept(this); + m_recordState.accept(state); + })) + .finallyDo( + () -> { + m_mechanism.m_drive.accept(Volts.of(0)); + m_recordState.accept(State.kNone); + }) + .withName("sysid-" + state.toString() + "-" + m_mechanism.m_name) + .withTimeout(m_config.m_timeout.in(Seconds)); + } +} diff --git a/wpilibNewCommands/src/main/native/cpp/frc2/command/sysid/SysIdRoutine.cpp b/wpilibNewCommands/src/main/native/cpp/frc2/command/sysid/SysIdRoutine.cpp new file mode 100644 index 00000000000..359c8d824d4 --- /dev/null +++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/sysid/SysIdRoutine.cpp @@ -0,0 +1,66 @@ +// 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 "frc2/command/sysid/SysIdRoutine.h" + +#include + +using namespace frc2::sysid; + +frc2::CommandPtr SysIdRoutine::Quasistatic(Direction direction) { + frc::sysid::State state; + if (direction == Direction::kForward) { + state = frc::sysid::State::kQuasistaticForward; + } else { // if (direction == Direction::kReverse) { + state = frc::sysid::State::kQuasistaticReverse; + } + + double outputSign = direction == Direction::kForward ? 1.0 : -1.0; + + return m_mechanism.m_subsystem->RunOnce([this] { timer.Restart(); }) + .AndThen( + m_mechanism.m_subsystem + ->Run([this, state, outputSign] { + m_outputVolts = outputSign * timer.Get() * m_config.m_rampRate; + m_mechanism.m_drive(m_outputVolts); + m_mechanism.m_log(this); + m_recordState(state); + }) + .FinallyDo([this] { + m_mechanism.m_drive(0_V); + m_recordState(frc::sysid::State::kNone); + timer.Stop(); + }) + .WithName("sysid-" + + frc::sysid::SysIdRoutineLog::StateEnumToString(state) + + "-" + m_mechanism.m_name) + .WithTimeout(m_config.m_timeout)); +} + +frc2::CommandPtr SysIdRoutine::Dynamic(Direction direction) { + frc::sysid::State state; + if (direction == Direction::kForward) { + state = frc::sysid::State::kDynamicForward; + } else { // if (direction == Direction::kReverse) { + state = frc::sysid::State::kDynamicReverse; + } + + double outputSign = direction == Direction::kForward ? 1.0 : -1.0; + + return m_mechanism.m_subsystem + ->RunOnce([this] { m_outputVolts = m_config.m_stepVoltage; }) + .AndThen(m_mechanism.m_subsystem->Run([this, state, outputSign] { + m_mechanism.m_drive(m_outputVolts * outputSign); + m_mechanism.m_log(this); + m_recordState(state); + })) + .FinallyDo([this] { + m_mechanism.m_drive(0_V); + m_recordState(frc::sysid::State::kNone); + }) + .WithName("sysid-" + + frc::sysid::SysIdRoutineLog::StateEnumToString(state) + "-" + + m_mechanism.m_name) + .WithTimeout(m_config.m_timeout); +} diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/Command.h b/wpilibNewCommands/src/main/native/include/frc2/command/Command.h index 27f5f184072..ad6aa79040c 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/Command.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/Command.h @@ -426,6 +426,7 @@ class Command : public wpi::Sendable, public wpi::SendableHelper { protected: Command(); + /// Requirements set. wpi::SmallSet m_requirements; /** diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/MecanumControllerCommand.h b/wpilibNewCommands/src/main/native/include/frc2/command/MecanumControllerCommand.h index e189ef1b95e..c305054d1ac 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/MecanumControllerCommand.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/MecanumControllerCommand.h @@ -2,7 +2,6 @@ // 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 #include #include diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/PIDCommand.h b/wpilibNewCommands/src/main/native/include/frc2/command/PIDCommand.h index bede6d0d514..ab16e7dd25d 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/PIDCommand.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/PIDCommand.h @@ -74,9 +74,16 @@ class PIDCommand : public CommandHelper { frc::PIDController& GetController(); protected: + /// PID controller. frc::PIDController m_controller; + + /// Measurement getter. std::function m_measurement; + + /// Setpoint getter. std::function m_setpoint; + + /// PID controller output consumer. std::function m_useOutput; }; } // namespace frc2 diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/PIDSubsystem.h b/wpilibNewCommands/src/main/native/include/frc2/command/PIDSubsystem.h index af61430e8a9..af7d2980874 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/PIDSubsystem.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/PIDSubsystem.h @@ -69,7 +69,10 @@ class PIDSubsystem : public SubsystemBase { frc::PIDController& GetController(); protected: + /// PID controller. frc::PIDController m_controller; + + /// Whether PID controller output is enabled. bool m_enabled{false}; /** diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/ParallelCommandGroup.h b/wpilibNewCommands/src/main/native/include/frc2/command/ParallelCommandGroup.h index bb33a059fad..05768223606 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/ParallelCommandGroup.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/ParallelCommandGroup.h @@ -9,7 +9,6 @@ #pragma warning(disable : 4521) #endif -#include #include #include #include @@ -17,7 +16,6 @@ #include -#include "frc2/command/CommandBase.h" #include "frc2/command/CommandHelper.h" namespace frc2 { diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDCommand.h b/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDCommand.h index 9ea5db5c7e3..6fbb12b1994 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDCommand.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDCommand.h @@ -139,9 +139,16 @@ class ProfiledPIDCommand frc::ProfiledPIDController& GetController() { return m_controller; } protected: + /// Profiled PID controller. frc::ProfiledPIDController m_controller; + + /// Measurement getter. std::function m_measurement; + + /// Goal getter. std::function m_goal; + + /// Profiled PID controller output consumer. std::function m_useOutput; }; } // namespace frc2 diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDSubsystem.h b/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDSubsystem.h index cfdfc6b6313..1dac7fab276 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDSubsystem.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDSubsystem.h @@ -91,7 +91,10 @@ class ProfiledPIDSubsystem : public SubsystemBase { frc::ProfiledPIDController& GetController() { return m_controller; } protected: + /// Profiled PID controller. frc::ProfiledPIDController m_controller; + + /// Whether the profiled PID controller output is enabled. bool m_enabled{false}; /** diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/SwerveControllerCommand.h b/wpilibNewCommands/src/main/native/include/frc2/command/SwerveControllerCommand.h index ae928b8b4a9..388730f89c3 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/SwerveControllerCommand.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/SwerveControllerCommand.h @@ -2,9 +2,7 @@ // 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 #include -#include #include #include diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileSubsystem.h b/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileSubsystem.h index 344aefc77ea..56680103122 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileSubsystem.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileSubsystem.h @@ -45,7 +45,7 @@ class TrapezoidProfileSubsystem : public SubsystemBase { m_period(period) {} void Periodic() override { - m_state = m_profile.Calculate(m_period, m_goal, m_state); + m_state = m_profile.Calculate(m_period, m_state, m_goal); if (m_enabled) { UseState(m_state); } diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/WaitCommand.h b/wpilibNewCommands/src/main/native/include/frc2/command/WaitCommand.h index e28615e468b..6f6db40667c 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/WaitCommand.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/WaitCommand.h @@ -41,6 +41,7 @@ class WaitCommand : public CommandHelper { void InitSendable(wpi::SendableBuilder& builder) override; protected: + /// The timer used for waiting. frc::Timer m_timer; private: diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/WrapperCommand.h b/wpilibNewCommands/src/main/native/include/frc2/command/WrapperCommand.h index 98e8b200820..97a8693dc0b 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/WrapperCommand.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/WrapperCommand.h @@ -69,6 +69,7 @@ class WrapperCommand : public CommandHelper { wpi::SmallSet GetRequirements() const override; protected: + /// Command being wrapped. std::unique_ptr m_command; }; } // namespace frc2 diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h index 4438aacdf90..3051a921ef7 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h @@ -4,9 +4,7 @@ #pragma once -#include #include -#include #include #include diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/sysid/SysIdRoutine.h b/wpilibNewCommands/src/main/native/include/frc2/command/sysid/SysIdRoutine.h new file mode 100644 index 00000000000..fe38971d63e --- /dev/null +++ b/wpilibNewCommands/src/main/native/include/frc2/command/sysid/SysIdRoutine.h @@ -0,0 +1,200 @@ +// 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 +#include +#include +#include + +#include +#include + +#include "frc2/command/CommandPtr.h" +#include "frc2/command/Subsystem.h" + +namespace frc2::sysid { + +using ramp_rate_t = units::unit_t< + units::compound_unit>>; + +/** Hardware-independent configuration for a SysId test routine. */ +class Config { + public: + /// The voltage ramp rate used for quasistatic test routines. + ramp_rate_t m_rampRate{1_V / 1_s}; + + /// The step voltage output used for dynamic test routines. + units::volt_t m_stepVoltage{7_V}; + + /// Safety timeout for the test routine commands. + units::second_t m_timeout{10_s}; + + /// Optional handle for recording test state in a third-party logging + /// solution. + std::function m_recordState; + + /** + * Create a new configuration for a SysId test routine. + * + * @param rampRate The voltage ramp rate used for quasistatic test routines. + * Defaults to 1 volt per second if left null. + * @param stepVoltage The step voltage output used for dynamic test routines. + * Defaults to 7 volts if left null. + * @param timeout Safety timeout for the test routine commands. Defaults to 10 + * seconds if left null. + * @param recordState Optional handle for recording test state in a + * third-party logging solution. If provided, the test routine state will be + * passed to this callback instead of logged in WPILog. + */ + Config(std::optional rampRate, + std::optional stepVoltage, + std::optional timeout, + std::optional> recordState) { + if (rampRate) { + m_rampRate = rampRate.value(); + } + if (stepVoltage) { + m_stepVoltage = stepVoltage.value(); + } + if (timeout) { + m_timeout = timeout.value(); + } + if (recordState) { + m_recordState = recordState.value(); + } + } +}; + +class Mechanism { + public: + /// Sends the SysId-specified drive signal to the mechanism motors during test + /// routines. + std::function m_drive; + + /// Returns measured data (voltages, positions, velocities) of the mechanism + /// motors during test routines. + std::function m_log; + + /// The subsystem containing the motor(s) that is (or are) being + /// characterized. + frc2::Subsystem* m_subsystem; + + /// The name of the mechanism being tested. Will be appended to the log entry + /// title for the routine's test state, e.g. "sysid-test-state-mechanism". + std::string m_name; + + /** + * Create a new mechanism specification for a SysId routine. + * + * @param drive Sends the SysId-specified drive signal to the mechanism motors + * during test routines. + * @param log Returns measured data of the mechanism motors during test + * routines. To return data, call `Motor(string motorName)` on the supplied + * `SysIdRoutineLog` instance, and then call one or more of the chainable + * logging handles (e.g. `voltage`) on the returned `MotorLog`. Multiple + * motors can be logged in a single callback by calling `Motor` multiple + * times. + * @param subsystem The subsystem containing the motor(s) that is (or are) + * being characterized. Will be declared as a requirement for the returned + * test commands. + * @param name The name of the mechanism being tested. Will be appended to the + * log entry * title for the routine's test state, e.g. + * "sysid-test-state-mechanism". Defaults to the name of the subsystem if + * left null. + */ + Mechanism(std::function drive, + std::function log, + frc2::Subsystem* subsystem, std::string_view name) + : m_drive{std::move(drive)}, + m_log{std::move(log)}, + m_subsystem{subsystem}, + m_name{name} {} + + /** + * Create a new mechanism specification for a SysId routine. Defaults the + * mechanism name to the subsystem name. + * + * @param drive Sends the SysId-specified drive signal to the mechanism motors + * during test routines. + * @param log Returns measured data of the mechanism motors during test + * routines. To return data, call `Motor(string motorName)` on the supplied + * `SysIdRoutineLog` instance, and then call one or more of the chainable + * logging handles (e.g. `voltage`) on the returned `MotorLog`. Multiple + * motors can be logged in a single callback by calling `Motor` multiple + * times. + * @param subsystem The subsystem containing the motor(s) that is (or are) + * being characterized. Will be declared as a requirement for the returned + * test commands. The subsystem's `name` will be appended to the log entry + * title for the routine's test state, e.g. "sysid-test-state-subsystem". + */ + Mechanism(std::function drive, + std::function log, + frc2::Subsystem* subsystem) + : m_drive{std::move(drive)}, + m_log{std::move(log)}, + m_subsystem{subsystem}, + m_name{m_subsystem->GetName()} {} +}; + +/** + * Motor direction for a SysId test. + */ +enum Direction { + /// Forward. + kForward, + /// Reverse. + kReverse +}; + +/** + * A SysId characterization routine for a single mechanism. Mechanisms may have + * multiple motors. + * + * A single subsystem may have multiple mechanisms, but mechanisms should not + * share test routines. Each complete test of a mechanism should have its own + * SysIdRoutine instance, since the log name of the recorded data is determined + * by the mechanism name. + * + * The test state (e.g. "quasistatic-forward") is logged once per iteration + * during test execution, and once with state "none" when a test ends. Motor + * frames are logged every iteration during test execution. + * + * Timestamps are not coordinated across data, so motor frames and test state + * tags may be recorded on different log frames. Because frame alignment is not + * guaranteed, SysId parses the log by using the test state flag to determine + * the timestamp range for each section of the test, and then extracts the motor + * frames within the valid timestamp ranges. If a given test was run multiple + * times in a single logfile, the user will need to select which of the tests to + * use for the fit in the analysis tool. + */ +class SysIdRoutine : public frc::sysid::SysIdRoutineLog { + public: + /** + * Create a new SysId characterization routine. + * + * @param config Hardware-independent parameters for the SysId routine. + * @param mechanism Hardware interface for the SysId routine. + */ + SysIdRoutine(Config config, Mechanism mechanism) + : SysIdRoutineLog(mechanism.m_name), + m_config(config), + m_mechanism(mechanism), + m_recordState(config.m_recordState ? config.m_recordState + : [this](frc::sysid::State state) { + this->RecordState(state); + }) {} + + frc2::CommandPtr Quasistatic(Direction direction); + frc2::CommandPtr Dynamic(Direction direction); + + private: + Config m_config; + Mechanism m_mechanism; + units::volt_t m_outputVolts{0}; + std::function m_recordState; + frc::Timer timer; +}; +} // namespace frc2::sysid diff --git a/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/sysid/SysIdRoutineTest.java b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/sysid/SysIdRoutineTest.java new file mode 100644 index 00000000000..3d885b21bd9 --- /dev/null +++ b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/sysid/SysIdRoutineTest.java @@ -0,0 +1,144 @@ +// 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.wpilibj2.command.sysid; + +import static edu.wpi.first.units.Units.Volts; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import edu.wpi.first.hal.HAL; +import edu.wpi.first.units.Measure; +import edu.wpi.first.units.Voltage; +import edu.wpi.first.wpilibj.simulation.SimHooks; +import edu.wpi.first.wpilibj.sysid.SysIdRoutineLog; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Subsystem; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SysIdRoutineTest { + interface Mechanism extends Subsystem { + void recordState(SysIdRoutineLog.State state); + + void drive(Measure voltage); + + void log(SysIdRoutineLog log); + } + + Mechanism m_mechanism; + SysIdRoutine m_sysidRoutine; + Command m_quasistaticForward; + Command m_quasistaticReverse; + Command m_dynamicForward; + Command m_dynamicReverse; + + void runCommand(Command command) { + command.initialize(); + command.execute(); + command.execute(); + SimHooks.stepTiming(1); + command.execute(); + command.end(true); + } + + @BeforeEach + void setup() { + HAL.initialize(500, 0); + SimHooks.pauseTiming(); + m_mechanism = mock(Mechanism.class); + m_sysidRoutine = + new SysIdRoutine( + new SysIdRoutine.Config(null, null, null, m_mechanism::recordState), + new SysIdRoutine.Mechanism(m_mechanism::drive, m_mechanism::log, new Subsystem() {})); + m_quasistaticForward = m_sysidRoutine.quasistatic(SysIdRoutine.Direction.kForward); + m_quasistaticReverse = m_sysidRoutine.quasistatic(SysIdRoutine.Direction.kReverse); + m_dynamicForward = m_sysidRoutine.dynamic(SysIdRoutine.Direction.kForward); + m_dynamicReverse = m_sysidRoutine.dynamic(SysIdRoutine.Direction.kReverse); + } + + @AfterEach + void cleanupAll() { + SimHooks.resumeTiming(); + } + + @Test + void recordStateBookendsMotorLogging() { + runCommand(m_quasistaticForward); + + var orderCheck = inOrder(m_mechanism); + + orderCheck.verify(m_mechanism).recordState(SysIdRoutineLog.State.kQuasistaticForward); + orderCheck.verify(m_mechanism).drive(any()); + orderCheck.verify(m_mechanism).log(any()); + orderCheck.verify(m_mechanism).recordState(SysIdRoutineLog.State.kNone); + orderCheck.verifyNoMoreInteractions(); + + clearInvocations(m_mechanism); + orderCheck = inOrder(m_mechanism); + runCommand(m_dynamicForward); + + orderCheck.verify(m_mechanism).recordState(SysIdRoutineLog.State.kDynamicForward); + orderCheck.verify(m_mechanism).drive(any()); + orderCheck.verify(m_mechanism).log(any()); + orderCheck.verify(m_mechanism).recordState(SysIdRoutineLog.State.kNone); + orderCheck.verifyNoMoreInteractions(); + } + + @Test + void testsDeclareCorrectState() { + runCommand(m_quasistaticForward); + verify(m_mechanism, atLeastOnce()).recordState(SysIdRoutineLog.State.kQuasistaticForward); + + runCommand(m_quasistaticReverse); + verify(m_mechanism, atLeastOnce()).recordState(SysIdRoutineLog.State.kQuasistaticReverse); + + runCommand(m_dynamicForward); + verify(m_mechanism, atLeastOnce()).recordState(SysIdRoutineLog.State.kDynamicForward); + + runCommand(m_dynamicReverse); + verify(m_mechanism, atLeastOnce()).recordState(SysIdRoutineLog.State.kDynamicReverse); + } + + @Test + void testsOutputCorrectVoltage() { + runCommand(m_quasistaticForward); + var orderCheck = inOrder(m_mechanism); + + orderCheck.verify(m_mechanism, atLeastOnce()).drive(Volts.of(1)); + orderCheck.verify(m_mechanism).drive(Volts.of(0)); + orderCheck.verify(m_mechanism, never()).drive(any()); + + clearInvocations(m_mechanism); + runCommand(m_quasistaticReverse); + orderCheck = inOrder(m_mechanism); + + orderCheck.verify(m_mechanism, atLeastOnce()).drive(Volts.of(-1)); + orderCheck.verify(m_mechanism).drive(Volts.of(0)); + orderCheck.verify(m_mechanism, never()).drive(any()); + + clearInvocations(m_mechanism); + runCommand(m_dynamicForward); + orderCheck = inOrder(m_mechanism); + + orderCheck.verify(m_mechanism, atLeastOnce()).drive(Volts.of(7)); + orderCheck.verify(m_mechanism).drive(Volts.of(0)); + orderCheck.verify(m_mechanism, never()).drive(any()); + + clearInvocations(m_mechanism); + runCommand(m_dynamicForward); + orderCheck = inOrder(m_mechanism); + + runCommand(m_dynamicReverse); + orderCheck.verify(m_mechanism, atLeastOnce()).drive(Volts.of(-7)); + orderCheck.verify(m_mechanism).drive(Volts.of(0)); + orderCheck.verify(m_mechanism, never()).drive(any()); + } +} diff --git a/wpilibNewCommands/src/test/native/cpp/frc2/command/sysid/SysIdRoutineTest.cpp b/wpilibNewCommands/src/test/native/cpp/frc2/command/sysid/SysIdRoutineTest.cpp new file mode 100644 index 00000000000..5e4a5825ac3 --- /dev/null +++ b/wpilibNewCommands/src/test/native/cpp/frc2/command/sysid/SysIdRoutineTest.cpp @@ -0,0 +1,170 @@ +// 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 +#include + +#include + +#include +#include +#include +#include + +#define EXPECT_NEAR_UNITS(val1, val2, eps) \ + EXPECT_LE(units::math::abs(val1 - val2), eps) + +enum StateTest { + Invalid, + InRecordStateQf, + InRecordStateQr, + InRecordStateDf, + InRecordStateDr, + InDrive, + InLog, + DoneWithRecordState +}; + +class SysIdRoutineTest : public ::testing::Test { + protected: + std::vector currentStateList{}; + std::vector sentVoltages{}; + frc2::Subsystem m_subsystem{}; + frc2::sysid::SysIdRoutine m_sysidRoutine{ + frc2::sysid::Config{ + std::nullopt, std::nullopt, std::nullopt, + [this](frc::sysid::State state) { + switch (state) { + case frc::sysid::State::kQuasistaticForward: + currentStateList.emplace_back(StateTest::InRecordStateQf); + break; + case frc::sysid::State::kQuasistaticReverse: + currentStateList.emplace_back(StateTest::InRecordStateQr); + break; + case frc::sysid::State::kDynamicForward: + currentStateList.emplace_back(StateTest::InRecordStateDf); + break; + case frc::sysid::State::kDynamicReverse: + currentStateList.emplace_back(StateTest::InRecordStateDr); + break; + case frc::sysid::State::kNone: + currentStateList.emplace_back(StateTest::DoneWithRecordState); + break; + } + }}, + frc2::sysid::Mechanism{ + [this](units::volt_t driveVoltage) { + sentVoltages.emplace_back(driveVoltage); + currentStateList.emplace_back(StateTest::InDrive); + }, + [this](frc::sysid::SysIdRoutineLog* log) { + currentStateList.emplace_back(StateTest::InLog); + log->Motor("Mock Motor").position(0_m).velocity(0_mps).voltage(0_V); + }, + &m_subsystem}}; + frc2::CommandPtr m_quasistaticForward{ + m_sysidRoutine.Quasistatic(frc2::sysid::Direction::kForward)}; + frc2::CommandPtr m_quasistaticReverse{ + m_sysidRoutine.Quasistatic(frc2::sysid::Direction::kReverse)}; + frc2::CommandPtr m_dynamicForward{ + m_sysidRoutine.Dynamic(frc2::sysid::Direction::kForward)}; + frc2::CommandPtr m_dynamicReverse{ + m_sysidRoutine.Dynamic(frc2::sysid::Direction::kReverse)}; + + void RunCommand(frc2::CommandPtr command) { + command.get()->Initialize(); + command.get()->Execute(); + frc::sim::StepTiming(1_s); + command.get()->Execute(); + command.get()->End(true); + } + + void SetUp() override { + frc::sim::PauseTiming(); + frc2::CommandPtr m_quasistaticForward{ + m_sysidRoutine.Quasistatic(frc2::sysid::Direction::kForward)}; + frc2::CommandPtr m_quasistaticReverse{ + m_sysidRoutine.Quasistatic(frc2::sysid::Direction::kReverse)}; + frc2::CommandPtr m_dynamicForward{ + m_sysidRoutine.Dynamic(frc2::sysid::Direction::kForward)}; + frc2::CommandPtr m_dynamicReverse{ + m_sysidRoutine.Dynamic(frc2::sysid::Direction::kReverse)}; + } + + void TearDown() override { frc::sim::ResumeTiming(); } +}; + +TEST_F(SysIdRoutineTest, RecordStateBookendsMotorLogging) { + RunCommand(std::move(m_quasistaticForward)); + std::vector expectedOrder{ + StateTest::InDrive, StateTest::InLog, StateTest::InRecordStateQf, + StateTest::InDrive, StateTest::DoneWithRecordState}; + EXPECT_TRUE(expectedOrder == currentStateList); + currentStateList.clear(); + sentVoltages.clear(); + + expectedOrder = std::vector{ + StateTest::InDrive, StateTest::InLog, StateTest::InRecordStateDf, + StateTest::InDrive, StateTest::DoneWithRecordState}; + RunCommand(std::move(m_dynamicForward)); + EXPECT_TRUE(expectedOrder == currentStateList); + currentStateList.clear(); + sentVoltages.clear(); +} + +TEST_F(SysIdRoutineTest, DeclareCorrectState) { + RunCommand(std::move(m_quasistaticForward)); + EXPECT_TRUE(std::find(currentStateList.begin(), currentStateList.end(), + StateTest::InRecordStateQf) != currentStateList.end()); + currentStateList.clear(); + sentVoltages.clear(); + + RunCommand(std::move(m_quasistaticReverse)); + EXPECT_TRUE(std::find(currentStateList.begin(), currentStateList.end(), + StateTest::InRecordStateQr) != currentStateList.end()); + currentStateList.clear(); + sentVoltages.clear(); + + RunCommand(std::move(m_dynamicForward)); + EXPECT_TRUE(std::find(currentStateList.begin(), currentStateList.end(), + StateTest::InRecordStateDf) != currentStateList.end()); + currentStateList.clear(); + sentVoltages.clear(); + + RunCommand(std::move(m_dynamicReverse)); + EXPECT_TRUE(std::find(currentStateList.begin(), currentStateList.end(), + StateTest::InRecordStateDr) != currentStateList.end()); + currentStateList.clear(); + sentVoltages.clear(); +} + +TEST_F(SysIdRoutineTest, OutputCorrectVoltage) { + RunCommand(std::move(m_quasistaticForward)); + std::vector expectedVoltages{1_V, 0_V}; + EXPECT_NEAR_UNITS(expectedVoltages[0], sentVoltages[0], 1e-6_V); + EXPECT_NEAR_UNITS(expectedVoltages[1], sentVoltages[1], 1e-6_V); + currentStateList.clear(); + sentVoltages.clear(); + + RunCommand(std::move(m_quasistaticReverse)); + expectedVoltages = std::vector{-1_V, 0_V}; + EXPECT_NEAR_UNITS(expectedVoltages[0], sentVoltages[0], 1e-6_V); + EXPECT_NEAR_UNITS(expectedVoltages[1], sentVoltages[1], 1e-6_V); + currentStateList.clear(); + sentVoltages.clear(); + + RunCommand(std::move(m_dynamicForward)); + expectedVoltages = std::vector{7_V, 0_V}; + EXPECT_NEAR_UNITS(expectedVoltages[0], sentVoltages[0], 1e-6_V); + EXPECT_NEAR_UNITS(expectedVoltages[1], sentVoltages[1], 1e-6_V); + currentStateList.clear(); + sentVoltages.clear(); + + RunCommand(std::move(m_dynamicReverse)); + expectedVoltages = std::vector{-7_V, 0_V}; + EXPECT_NEAR_UNITS(expectedVoltages[0], sentVoltages[0], 1e-6_V); + EXPECT_NEAR_UNITS(expectedVoltages[1], sentVoltages[1], 1e-6_V); + currentStateList.clear(); + sentVoltages.clear(); +} diff --git a/wpilibNewCommands/wpilibNewCommands-config.cmake.in b/wpilibNewCommands/wpilibNewCommands-config.cmake.in deleted file mode 100644 index 7f33a403d10..00000000000 --- a/wpilibNewCommands/wpilibNewCommands-config.cmake.in +++ /dev/null @@ -1,14 +0,0 @@ -include(CMakeFindDependencyMacro) - @WPIUTIL_DEP_REPLACE@ - @NTCORE_DEP_REPLACE@ - @CSCORE_DEP_REPLACE@ - @CAMERASERVER_DEP_REPLACE@ - @HAL_DEP_REPLACE@ - @WPILIBC_DEP_REPLACE@ - @WPIMATH_DEP_REPLACE@ - - @FILENAME_DEP_REPLACE@ - include(${SELF_DIR}/wpilibNewCommands.cmake) - if(@WITH_JAVA@) - include(${SELF_DIR}/wpilibNewCommands_jar.cmake) - endif() diff --git a/wpilibNewCommands/wpilibnewcommands-config.cmake.in b/wpilibNewCommands/wpilibnewcommands-config.cmake.in new file mode 100644 index 00000000000..b26b9a7a555 --- /dev/null +++ b/wpilibNewCommands/wpilibnewcommands-config.cmake.in @@ -0,0 +1,14 @@ +include(CMakeFindDependencyMacro) +@WPIUTIL_DEP_REPLACE@ +@NTCORE_DEP_REPLACE@ +@CSCORE_DEP_REPLACE@ +@CAMERASERVER_DEP_REPLACE@ +@HAL_DEP_REPLACE@ +@WPILIBC_DEP_REPLACE@ +@WPIMATH_DEP_REPLACE@ + +@FILENAME_DEP_REPLACE@ +include(${SELF_DIR}/wpilibnewcommands.cmake) +if(@WITH_JAVA@) + include(${SELF_DIR}/wpilibNewCommands_jar.cmake) +endif() diff --git a/wpilibc/src/generate/WPILibVersion.cpp.in b/wpilibc/src/generate/WPILibVersion.cpp.in index b0a44905207..cfe24411588 100644 --- a/wpilibc/src/generate/WPILibVersion.cpp.in +++ b/wpilibc/src/generate/WPILibVersion.cpp.in @@ -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. */ diff --git a/wpilibc/src/main/native/cpp/ADIS16448_IMU.cpp b/wpilibc/src/main/native/cpp/ADIS16448_IMU.cpp index 14fd37fedc1..a9950ae2dbe 100644 --- a/wpilibc/src/main/native/cpp/ADIS16448_IMU.cpp +++ b/wpilibc/src/main/native/cpp/ADIS16448_IMU.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -155,6 +154,122 @@ ADIS16448_IMU::ADIS16448_IMU(IMUAxis yaw_axis, SPI::Port port, m_connected = true; } +ADIS16448_IMU::ADIS16448_IMU(ADIS16448_IMU&& other) + : m_reset_in{std::move(other.m_reset_in)}, + m_status_led{std::move(other.m_status_led)}, + m_yaw_axis{std::move(other.m_yaw_axis)}, + m_gyro_rate_x{std::move(other.m_gyro_rate_x)}, + m_gyro_rate_y{std::move(other.m_gyro_rate_y)}, + m_gyro_rate_z{std::move(other.m_gyro_rate_z)}, + m_accel_x{std::move(other.m_accel_x)}, + m_accel_y{std::move(other.m_accel_y)}, + m_accel_z{std::move(other.m_accel_z)}, + m_mag_x{std::move(other.m_mag_x)}, + m_mag_y{std::move(other.m_mag_y)}, + m_mag_z{std::move(other.m_mag_z)}, + m_baro{std::move(other.m_baro)}, + m_temp{std::move(other.m_temp)}, + m_tau{std::move(other.m_tau)}, + m_dt{std::move(other.m_dt)}, + m_alpha{std::move(other.m_alpha)}, + m_compAngleX{std::move(other.m_compAngleX)}, + m_compAngleY{std::move(other.m_compAngleY)}, + m_accelAngleX{std::move(other.m_accelAngleX)}, + m_accelAngleY{std::move(other.m_accelAngleY)}, + m_offset_buffer{other.m_offset_buffer}, + m_gyro_rate_offset_x{std::move(other.m_gyro_rate_offset_x)}, + m_gyro_rate_offset_y{std::move(other.m_gyro_rate_offset_y)}, + m_gyro_rate_offset_z{std::move(other.m_gyro_rate_offset_z)}, + m_avg_size{std::move(other.m_avg_size)}, + m_accum_count{std::move(other.m_accum_count)}, + m_integ_gyro_angle_x{std::move(other.m_integ_gyro_angle_x)}, + m_integ_gyro_angle_y{std::move(other.m_integ_gyro_angle_y)}, + m_integ_gyro_angle_z{std::move(other.m_integ_gyro_angle_z)}, + m_thread_active{other.m_thread_active.load()}, + m_first_run{other.m_first_run.load()}, + m_thread_idle{other.m_thread_idle.load()}, + m_start_up_mode{other.m_start_up_mode.load()}, + m_auto_configured{std::move(other.m_auto_configured)}, + m_spi_port{std::move(other.m_spi_port)}, + m_calibration_time{std::move(other.m_calibration_time)}, + m_spi{std::move(other.m_spi)}, + m_auto_interrupt{std::move(other.m_auto_interrupt)}, + m_connected{std::move(other.m_connected)}, + m_acquire_task{std::move(other.m_acquire_task)}, + m_simDevice{std::move(other.m_simDevice)}, + m_simConnected{std::move(other.m_simConnected)}, + m_simGyroAngleX{std::move(other.m_simGyroAngleX)}, + m_simGyroAngleY{std::move(other.m_simGyroAngleZ)}, + m_simGyroAngleZ{std::move(other.m_simGyroAngleZ)}, + m_simGyroRateX{std::move(other.m_simGyroRateX)}, + m_simGyroRateY{std::move(other.m_simGyroRateY)}, + m_simGyroRateZ{std::move(other.m_simGyroRateZ)}, + m_simAccelX{std::move(other.m_simAccelX)}, + m_simAccelY{std::move(other.m_simAccelY)}, + m_simAccelZ{std::move(other.m_simAccelZ)}, + m_mutex{std::move(other.m_mutex)} {} + +ADIS16448_IMU& ADIS16448_IMU::operator=(ADIS16448_IMU&& other) { + if (this == &other) { + return *this; + } + + std::swap(this->m_reset_in, other.m_reset_in); + std::swap(this->m_status_led, other.m_status_led); + std::swap(this->m_yaw_axis, other.m_yaw_axis); + std::swap(this->m_gyro_rate_x, other.m_gyro_rate_x); + std::swap(this->m_gyro_rate_y, other.m_gyro_rate_y); + std::swap(this->m_gyro_rate_z, other.m_gyro_rate_z); + std::swap(this->m_accel_x, other.m_accel_x); + std::swap(this->m_accel_y, other.m_accel_y); + std::swap(this->m_accel_z, other.m_accel_z); + std::swap(this->m_mag_x, other.m_mag_x); + std::swap(this->m_mag_y, other.m_mag_y); + std::swap(this->m_mag_z, other.m_mag_z); + std::swap(this->m_baro, other.m_baro); + std::swap(this->m_temp, other.m_temp); + std::swap(this->m_tau, other.m_tau); + std::swap(this->m_dt, other.m_dt); + std::swap(this->m_alpha, other.m_alpha); + std::swap(this->m_compAngleX, other.m_compAngleX); + std::swap(this->m_compAngleY, other.m_compAngleY); + std::swap(this->m_accelAngleX, other.m_accelAngleX); + std::swap(this->m_accelAngleY, other.m_accelAngleY); + std::swap(this->m_offset_buffer, other.m_offset_buffer); + std::swap(this->m_gyro_rate_offset_x, other.m_gyro_rate_offset_x); + std::swap(this->m_gyro_rate_offset_y, other.m_gyro_rate_offset_y); + std::swap(this->m_gyro_rate_offset_z, other.m_gyro_rate_offset_z); + std::swap(this->m_avg_size, other.m_avg_size); + std::swap(this->m_accum_count, other.m_accum_count); + std::swap(this->m_integ_gyro_angle_x, other.m_integ_gyro_angle_x); + std::swap(this->m_integ_gyro_angle_y, other.m_integ_gyro_angle_y); + std::swap(this->m_integ_gyro_angle_z, other.m_integ_gyro_angle_z); + this->m_thread_active = other.m_thread_active.load(); + this->m_first_run = other.m_first_run.load(); + this->m_thread_idle = other.m_thread_idle.load(); + this->m_start_up_mode = other.m_start_up_mode.load(); + std::swap(this->m_auto_configured, other.m_auto_configured); + std::swap(this->m_spi_port, other.m_spi_port); + std::swap(this->m_calibration_time, other.m_calibration_time); + std::swap(this->m_spi, other.m_spi); + std::swap(this->m_auto_interrupt, other.m_auto_interrupt); + std::swap(this->m_connected, other.m_connected); + std::swap(this->m_acquire_task, other.m_acquire_task); + std::swap(this->m_simDevice, other.m_simDevice); + std::swap(this->m_simConnected, other.m_simConnected); + std::swap(this->m_simGyroAngleX, other.m_simGyroAngleX); + std::swap(this->m_simGyroAngleY, other.m_simGyroAngleY); + std::swap(this->m_simGyroAngleZ, other.m_simGyroAngleZ); + std::swap(this->m_simGyroRateX, other.m_simGyroRateX); + std::swap(this->m_simGyroRateY, other.m_simGyroRateY); + std::swap(this->m_simGyroRateZ, other.m_simGyroRateZ); + std::swap(this->m_simAccelX, other.m_simAccelX); + std::swap(this->m_simAccelY, other.m_simAccelY); + std::swap(this->m_simAccelZ, other.m_simAccelZ); + std::swap(this->m_mutex, other.m_mutex); + return *this; +} + bool ADIS16448_IMU::IsConnected() const { if (m_simConnected) { return m_simConnected.Get(); @@ -679,8 +794,13 @@ double ADIS16448_IMU::CompFilterProcess(double compAngle, double accelAngle, return compAngle; } -int ADIS16448_IMU::ConfigDecRate(uint16_t DecimationSetting) { - uint16_t writeValue = DecimationSetting; +int ADIS16448_IMU::ConfigDecRate(uint16_t decimationRate) { + // Switches the active SPI port to standard SPI mode, writes a new value to + // the DECIMATE register in the IMU, and re-enables auto SPI. + // + // This function enters standard SPI mode, writes a new DECIMATE setting to + // the IMU, adjusts the sample scale factor, and re-enters auto SPI mode. + uint16_t writeValue = decimationRate; uint16_t readbackValue; if (!SwitchToStandardSPI()) { REPORT_ERROR("Failed to configure/reconfigure standard SPI."); @@ -688,14 +808,14 @@ int ADIS16448_IMU::ConfigDecRate(uint16_t DecimationSetting) { } /* Check max */ - if (DecimationSetting > 9) { + if (decimationRate > 9) { REPORT_ERROR( "Attempted to write an invalid decimation value. Capping at 9"); - DecimationSetting = 9; + decimationRate = 9; } /* Shift decimation setting to correct position and select internal sync */ - writeValue = (DecimationSetting << 8) | 0x1; + writeValue = (decimationRate << 8) | 0x1; /* Apply to IMU */ WriteRegister(SMPL_PRD, writeValue); diff --git a/wpilibc/src/main/native/cpp/ADIS16470_IMU.cpp b/wpilibc/src/main/native/cpp/ADIS16470_IMU.cpp index 4eaa7ccd609..6f562e1b6ed 100644 --- a/wpilibc/src/main/native/cpp/ADIS16470_IMU.cpp +++ b/wpilibc/src/main/native/cpp/ADIS16470_IMU.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include @@ -173,6 +173,104 @@ ADIS16470_IMU::ADIS16470_IMU(IMUAxis yaw_axis, IMUAxis pitch_axis, m_connected = true; } +ADIS16470_IMU::ADIS16470_IMU(ADIS16470_IMU&& other) + : m_yaw_axis{std::move(other.m_yaw_axis)}, + m_pitch_axis{std::move(other.m_pitch_axis)}, + m_roll_axis{std::move(other.m_roll_axis)}, + m_reset_in{std::move(other.m_reset_in)}, + m_status_led{std::move(other.m_status_led)}, + m_integ_angle_x{std::move(other.m_integ_angle_x)}, + m_integ_angle_y{std::move(other.m_integ_angle_y)}, + m_integ_angle_z{std::move(other.m_integ_angle_z)}, + m_gyro_rate_x{std::move(other.m_gyro_rate_x)}, + m_gyro_rate_y{std::move(other.m_gyro_rate_y)}, + m_gyro_rate_z{std::move(other.m_gyro_rate_z)}, + m_accel_x{std::move(other.m_accel_x)}, + m_accel_y{std::move(other.m_accel_y)}, + m_accel_z{std::move(other.m_accel_z)}, + m_tau{std::move(other.m_tau)}, + m_dt{std::move(other.m_dt)}, + m_alpha{std::move(other.m_alpha)}, + m_compAngleX{std::move(other.m_compAngleX)}, + m_compAngleY{std::move(other.m_compAngleY)}, + m_accelAngleX{std::move(other.m_accelAngleX)}, + m_accelAngleY{std::move(other.m_accelAngleY)}, + m_thread_active{other.m_thread_active.load()}, + m_first_run{other.m_first_run.load()}, + m_thread_idle{other.m_thread_idle.load()}, + m_auto_configured{std::move(other.m_auto_configured)}, + m_spi_port{std::move(other.m_spi_port)}, + m_calibration_time{std::move(other.m_calibration_time)}, + m_spi{std::move(other.m_spi)}, + m_auto_interrupt{std::move(other.m_auto_interrupt)}, + m_scaled_sample_rate{std::move(other.m_scaled_sample_rate)}, + m_connected{std::move(other.m_connected)}, + m_acquire_task{std::move(other.m_acquire_task)}, + m_simDevice{std::move(other.m_simDevice)}, + m_simConnected{std::move(other.m_simConnected)}, + m_simGyroAngleX{std::move(other.m_simGyroAngleX)}, + m_simGyroAngleY{std::move(other.m_simGyroAngleZ)}, + m_simGyroAngleZ{std::move(other.m_simGyroAngleZ)}, + m_simGyroRateX{std::move(other.m_simGyroRateX)}, + m_simGyroRateY{std::move(other.m_simGyroRateY)}, + m_simGyroRateZ{std::move(other.m_simGyroRateZ)}, + m_simAccelX{std::move(other.m_simAccelX)}, + m_simAccelY{std::move(other.m_simAccelY)}, + m_simAccelZ{std::move(other.m_simAccelZ)}, + m_mutex{std::move(other.m_mutex)} {} + +ADIS16470_IMU& ADIS16470_IMU::operator=(ADIS16470_IMU&& other) { + if (this == &other) { + return *this; + } + + std::swap(this->m_yaw_axis, other.m_yaw_axis); + std::swap(this->m_pitch_axis, other.m_pitch_axis); + std::swap(this->m_roll_axis, other.m_roll_axis); + std::swap(this->m_reset_in, other.m_reset_in); + std::swap(this->m_status_led, other.m_status_led); + std::swap(this->m_integ_angle_x, other.m_integ_angle_x); + std::swap(this->m_integ_angle_y, other.m_integ_angle_y); + std::swap(this->m_integ_angle_z, other.m_integ_angle_z); + std::swap(this->m_gyro_rate_x, other.m_gyro_rate_x); + std::swap(this->m_gyro_rate_y, other.m_gyro_rate_y); + std::swap(this->m_gyro_rate_z, other.m_gyro_rate_z); + std::swap(this->m_accel_x, other.m_accel_x); + std::swap(this->m_accel_y, other.m_accel_y); + std::swap(this->m_accel_z, other.m_accel_z); + std::swap(this->m_tau, other.m_tau); + std::swap(this->m_dt, other.m_dt); + std::swap(this->m_alpha, other.m_alpha); + std::swap(this->m_compAngleX, other.m_compAngleX); + std::swap(this->m_compAngleY, other.m_compAngleY); + std::swap(this->m_accelAngleX, other.m_accelAngleX); + std::swap(this->m_accelAngleY, other.m_accelAngleY); + this->m_thread_active = other.m_thread_active.load(); + this->m_first_run = other.m_first_run.load(); + this->m_thread_idle = other.m_thread_idle.load(); + std::swap(this->m_auto_configured, other.m_auto_configured); + std::swap(this->m_spi_port, other.m_spi_port); + std::swap(this->m_calibration_time, other.m_calibration_time); + std::swap(this->m_spi, other.m_spi); + std::swap(this->m_auto_interrupt, other.m_auto_interrupt); + std::swap(this->m_scaled_sample_rate, other.m_scaled_sample_rate); + std::swap(this->m_connected, other.m_connected); + std::swap(this->m_acquire_task, other.m_acquire_task); + std::swap(this->m_simDevice, other.m_simDevice); + std::swap(this->m_simConnected, other.m_simConnected); + std::swap(this->m_simGyroAngleX, other.m_simGyroAngleX); + std::swap(this->m_simGyroAngleY, other.m_simGyroAngleZ); + std::swap(this->m_simGyroAngleZ, other.m_simGyroAngleZ); + std::swap(this->m_simGyroRateX, other.m_simGyroRateX); + std::swap(this->m_simGyroRateY, other.m_simGyroRateY); + std::swap(this->m_simGyroRateZ, other.m_simGyroRateZ); + std::swap(this->m_simAccelX, other.m_simAccelX); + std::swap(this->m_simAccelY, other.m_simAccelY); + std::swap(this->m_simAccelZ, other.m_simAccelZ); + std::swap(this->m_mutex, other.m_mutex); + return *this; +} + bool ADIS16470_IMU::IsConnected() const { if (m_simConnected) { return m_simConnected.Get(); @@ -348,31 +446,22 @@ int ADIS16470_IMU::ConfigCalTime(CalibrationTime new_cal_time) { return 0; } -/** - * @brief Switches the active SPI port to standard SPI mode, writes a new value - *to the DECIMATE register in the IMU, and re-enables auto SPI. - * - * @param reg Decimation value to be set. - * - * @return An int indicating the success or failure of writing the new DECIMATE - *setting and returning to auto SPI mode. 0 = Success, 1 = No Change, 2 = - *Failure - * - * This function enters standard SPI mode, writes a new DECIMATE setting to the - *IMU, adjusts the sample scale factor, and re-enters auto SPI mode. - **/ -int ADIS16470_IMU::ConfigDecRate(uint16_t reg) { - uint16_t m_reg = reg; +int ADIS16470_IMU::ConfigDecRate(uint16_t decimationRate) { + // Switches the active SPI port to standard SPI mode, writes a new value to + // the DECIMATE register in the IMU, and re-enables auto SPI. + // + // This function enters standard SPI mode, writes a new DECIMATE setting to + // the IMU, adjusts the sample scale factor, and re-enters auto SPI mode. if (!SwitchToStandardSPI()) { REPORT_ERROR("Failed to configure/reconfigure standard SPI."); return 2; } - if (m_reg > 1999) { + if (decimationRate > 1999) { REPORT_ERROR("Attempted to write an invalid decimation value."); - m_reg = 1999; + decimationRate = 1999; } - m_scaled_sample_rate = (((m_reg + 1.0) / 2000.0) * 1000000.0); - WriteRegister(DEC_RATE, m_reg); + m_scaled_sample_rate = (((decimationRate + 1.0) / 2000.0) * 1000000.0); + WriteRegister(DEC_RATE, decimationRate); if (!SwitchToAutoSPI()) { REPORT_ERROR("Failed to configure/reconfigure auto SPI."); return 2; diff --git a/wpilibc/src/main/native/cpp/DMA.cpp b/wpilibc/src/main/native/cpp/DMA.cpp index ce6c5328a64..da45aa3562e 100644 --- a/wpilibc/src/main/native/cpp/DMA.cpp +++ b/wpilibc/src/main/native/cpp/DMA.cpp @@ -40,9 +40,9 @@ void DMA::SetPause(bool pause) { FRC_CheckErrorStatus(status, "SetPause"); } -void DMA::SetTimedTrigger(units::second_t seconds) { +void DMA::SetTimedTrigger(units::second_t period) { int32_t status = 0; - HAL_SetDMATimedTrigger(dmaHandle, seconds.value(), &status); + HAL_SetDMATimedTrigger(dmaHandle, period.value(), &status); FRC_CheckErrorStatus(status, "SetTimedTrigger"); } diff --git a/wpilibc/src/main/native/cpp/DigitalOutput.cpp b/wpilibc/src/main/native/cpp/DigitalOutput.cpp index fc69e359da5..9f65faf3fca 100644 --- a/wpilibc/src/main/native/cpp/DigitalOutput.cpp +++ b/wpilibc/src/main/native/cpp/DigitalOutput.cpp @@ -78,7 +78,7 @@ int DigitalOutput::GetChannel() const { void DigitalOutput::Pulse(units::second_t pulseLength) { int32_t status = 0; - HAL_Pulse(m_handle, pulseLength.to(), &status); + HAL_Pulse(m_handle, pulseLength.value(), &status); FRC_CheckErrorStatus(status, "Channel {}", m_channel); } diff --git a/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp b/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp index ee941b61cc0..3669bce78ee 100644 --- a/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp +++ b/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include "frc/DSControlWord.h" @@ -97,10 +98,16 @@ void IterativeRobotBase::SetNetworkTablesFlushEnabled(bool enabled) { } void IterativeRobotBase::EnableLiveWindowInTest(bool testLW) { + static bool hasReported; if (IsTestEnabled()) { throw FRC_MakeError(err::IncompatibleMode, "Can't configure test mode while in test mode!"); } + if (!hasReported && testLW) { + HAL_Report(HALUsageReporting::kResourceType_SmartDashboard, + HALUsageReporting::kSmartDashboard_LiveWindow); + hasReported = true; + } m_lwEnabledInTest = testLW; } diff --git a/wpilibc/src/main/native/cpp/counter/Tachometer.cpp b/wpilibc/src/main/native/cpp/counter/Tachometer.cpp index 90324f59c9a..90688d2c386 100644 --- a/wpilibc/src/main/native/cpp/counter/Tachometer.cpp +++ b/wpilibc/src/main/native/cpp/counter/Tachometer.cpp @@ -44,7 +44,7 @@ Tachometer::~Tachometer() { units::hertz_t Tachometer::GetFrequency() const { auto period = GetPeriod(); - if (period.to() == 0) { + if (period.value() == 0) { return units::hertz_t{0.0}; } return 1 / period; @@ -66,7 +66,7 @@ void Tachometer::SetEdgesPerRevolution(int edges) { units::turns_per_second_t Tachometer::GetRevolutionsPerSecond() const { auto period = GetPeriod(); - if (period.to() == 0) { + if (period.value() == 0) { return units::turns_per_second_t{0.0}; } int edgesPerRevolution = GetEdgesPerRevolution(); @@ -74,7 +74,7 @@ units::turns_per_second_t Tachometer::GetRevolutionsPerSecond() const { return units::turns_per_second_t{0.0}; } auto rotationHz = ((1.0 / edgesPerRevolution) / period); - return units::turns_per_second_t{rotationHz.to()}; + return units::turns_per_second_t{rotationHz.value()}; } units::revolutions_per_minute_t Tachometer::GetRevolutionsPerMinute() const { @@ -103,7 +103,7 @@ void Tachometer::SetSamplesToAverage(int samples) { void Tachometer::SetMaxPeriod(units::second_t maxPeriod) { int32_t status = 0; - HAL_SetCounterMaxPeriod(m_handle, maxPeriod.to(), &status); + HAL_SetCounterMaxPeriod(m_handle, maxPeriod.value(), &status); FRC_CheckErrorStatus(status, "Channel {}", m_source->GetChannel()); } @@ -116,7 +116,7 @@ void Tachometer::SetUpdateWhenEmpty(bool updateWhenEmpty) { void Tachometer::InitSendable(wpi::SendableBuilder& builder) { builder.SetSmartDashboardType("Tachometer"); builder.AddDoubleProperty( - "RPS", [&] { return GetRevolutionsPerSecond().to(); }, nullptr); + "RPS", [&] { return GetRevolutionsPerSecond().value(); }, nullptr); builder.AddDoubleProperty( - "RPM", [&] { return GetRevolutionsPerMinute().to(); }, nullptr); + "RPM", [&] { return GetRevolutionsPerMinute().value(); }, nullptr); } diff --git a/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp b/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp index b72b22b80c2..e3a0a8c4945 100644 --- a/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp +++ b/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp @@ -16,11 +16,21 @@ using namespace frc; +WPI_IGNORE_DEPRECATED + DifferentialDrive::DifferentialDrive(MotorController& leftMotor, MotorController& rightMotor) - : m_leftMotor(&leftMotor), m_rightMotor(&rightMotor) { - wpi::SendableRegistry::AddChild(this, m_leftMotor); - wpi::SendableRegistry::AddChild(this, m_rightMotor); + : DifferentialDrive{[&](double output) { leftMotor.Set(output); }, + [&](double output) { rightMotor.Set(output); }} { + wpi::SendableRegistry::AddChild(this, &leftMotor); + wpi::SendableRegistry::AddChild(this, &rightMotor); +} + +WPI_UNIGNORE_DEPRECATED + +DifferentialDrive::DifferentialDrive(std::function leftMotor, + std::function rightMotor) + : m_leftMotor{std::move(leftMotor)}, m_rightMotor{std::move(rightMotor)} { static int instances = 0; ++instances; wpi::SendableRegistry::AddLW(this, "DifferentialDrive", instances); @@ -40,8 +50,11 @@ void DifferentialDrive::ArcadeDrive(double xSpeed, double zRotation, auto [left, right] = ArcadeDriveIK(xSpeed, zRotation, squareInputs); - m_leftMotor->Set(left * m_maxOutput); - m_rightMotor->Set(right * m_maxOutput); + m_leftOutput = left * m_maxOutput; + m_rightOutput = right * m_maxOutput; + + m_leftMotor(m_leftOutput); + m_rightMotor(m_rightOutput); Feed(); } @@ -60,8 +73,11 @@ void DifferentialDrive::CurvatureDrive(double xSpeed, double zRotation, auto [left, right] = CurvatureDriveIK(xSpeed, zRotation, allowTurnInPlace); - m_leftMotor->Set(left * m_maxOutput); - m_rightMotor->Set(right * m_maxOutput); + m_leftOutput = left * m_maxOutput; + m_rightOutput = right * m_maxOutput; + + m_leftMotor(m_leftOutput); + m_rightMotor(m_rightOutput); Feed(); } @@ -80,8 +96,11 @@ void DifferentialDrive::TankDrive(double leftSpeed, double rightSpeed, auto [left, right] = TankDriveIK(leftSpeed, rightSpeed, squareInputs); - m_leftMotor->Set(left * m_maxOutput); - m_rightMotor->Set(right * m_maxOutput); + m_leftOutput = left * m_maxOutput; + m_rightOutput = right * m_maxOutput; + + m_leftMotor(m_leftOutput); + m_rightMotor(m_rightOutput); Feed(); } @@ -157,8 +176,12 @@ DifferentialDrive::WheelSpeeds DifferentialDrive::TankDriveIK( } void DifferentialDrive::StopMotor() { - m_leftMotor->StopMotor(); - m_rightMotor->StopMotor(); + m_leftOutput = 0.0; + m_rightOutput = 0.0; + + m_leftMotor(0.0); + m_rightMotor(0.0); + Feed(); } @@ -171,9 +194,7 @@ void DifferentialDrive::InitSendable(wpi::SendableBuilder& builder) { builder.SetActuator(true); builder.SetSafeState([=, this] { StopMotor(); }); builder.AddDoubleProperty( - "Left Motor Speed", [=, this] { return m_leftMotor->Get(); }, - [=, this](double value) { m_leftMotor->Set(value); }); + "Left Motor Speed", [&] { return m_leftOutput; }, m_leftMotor); builder.AddDoubleProperty( - "Right Motor Speed", [=, this] { return m_rightMotor->Get(); }, - [=, this](double value) { m_rightMotor->Set(value); }); + "Right Motor Speed", [&] { return m_rightOutput; }, m_rightMotor); } diff --git a/wpilibc/src/main/native/cpp/drive/MecanumDrive.cpp b/wpilibc/src/main/native/cpp/drive/MecanumDrive.cpp index 2bf6a3f3c49..aeec27d0ded 100644 --- a/wpilibc/src/main/native/cpp/drive/MecanumDrive.cpp +++ b/wpilibc/src/main/native/cpp/drive/MecanumDrive.cpp @@ -16,18 +16,32 @@ using namespace frc; +WPI_IGNORE_DEPRECATED + MecanumDrive::MecanumDrive(MotorController& frontLeftMotor, MotorController& rearLeftMotor, MotorController& frontRightMotor, MotorController& rearRightMotor) - : m_frontLeftMotor(&frontLeftMotor), - m_rearLeftMotor(&rearLeftMotor), - m_frontRightMotor(&frontRightMotor), - m_rearRightMotor(&rearRightMotor) { - wpi::SendableRegistry::AddChild(this, m_frontLeftMotor); - wpi::SendableRegistry::AddChild(this, m_rearLeftMotor); - wpi::SendableRegistry::AddChild(this, m_frontRightMotor); - wpi::SendableRegistry::AddChild(this, m_rearRightMotor); + : MecanumDrive{[&](double output) { frontLeftMotor.Set(output); }, + [&](double output) { rearLeftMotor.Set(output); }, + [&](double output) { frontRightMotor.Set(output); }, + [&](double output) { rearRightMotor.Set(output); }} { + wpi::SendableRegistry::AddChild(this, &frontLeftMotor); + wpi::SendableRegistry::AddChild(this, &rearLeftMotor); + wpi::SendableRegistry::AddChild(this, &frontRightMotor); + wpi::SendableRegistry::AddChild(this, &rearRightMotor); +} + +WPI_UNIGNORE_DEPRECATED + +MecanumDrive::MecanumDrive(std::function frontLeftMotor, + std::function rearLeftMotor, + std::function frontRightMotor, + std::function rearRightMotor) + : m_frontLeftMotor{std::move(frontLeftMotor)}, + m_rearLeftMotor{std::move(rearLeftMotor)}, + m_frontRightMotor{std::move(frontRightMotor)}, + m_rearRightMotor{std::move(rearRightMotor)} { static int instances = 0; ++instances; wpi::SendableRegistry::AddLW(this, "MecanumDrive", instances); @@ -47,10 +61,15 @@ void MecanumDrive::DriveCartesian(double xSpeed, double ySpeed, auto [frontLeft, frontRight, rearLeft, rearRight] = DriveCartesianIK(xSpeed, ySpeed, zRotation, gyroAngle); - m_frontLeftMotor->Set(frontLeft * m_maxOutput); - m_frontRightMotor->Set(frontRight * m_maxOutput); - m_rearLeftMotor->Set(rearLeft * m_maxOutput); - m_rearRightMotor->Set(rearRight * m_maxOutput); + m_frontLeftOutput = frontLeft * m_maxOutput; + m_rearLeftOutput = rearLeft * m_maxOutput; + m_frontRightOutput = frontRight * m_maxOutput; + m_rearRightOutput = rearRight * m_maxOutput; + + m_frontLeftMotor(m_frontLeftOutput); + m_frontRightMotor(m_frontRightOutput); + m_rearLeftMotor(m_rearLeftOutput); + m_rearRightMotor(m_rearRightOutput); Feed(); } @@ -68,10 +87,16 @@ void MecanumDrive::DrivePolar(double magnitude, Rotation2d angle, } void MecanumDrive::StopMotor() { - m_frontLeftMotor->StopMotor(); - m_frontRightMotor->StopMotor(); - m_rearLeftMotor->StopMotor(); - m_rearRightMotor->StopMotor(); + m_frontLeftOutput = 0.0; + m_frontRightOutput = 0.0; + m_rearLeftOutput = 0.0; + m_rearRightOutput = 0.0; + + m_frontLeftMotor(0.0); + m_frontRightMotor(0.0); + m_rearLeftMotor(0.0); + m_rearRightMotor(0.0); + Feed(); } @@ -108,15 +133,15 @@ void MecanumDrive::InitSendable(wpi::SendableBuilder& builder) { builder.SetActuator(true); builder.SetSafeState([=, this] { StopMotor(); }); builder.AddDoubleProperty( - "Front Left Motor Speed", [=, this] { return m_frontLeftMotor->Get(); }, - [=, this](double value) { m_frontLeftMotor->Set(value); }); + "Front Left Motor Speed", [&] { return m_frontLeftOutput; }, + m_frontLeftMotor); builder.AddDoubleProperty( - "Front Right Motor Speed", [=, this] { return m_frontRightMotor->Get(); }, - [=, this](double value) { m_frontRightMotor->Set(value); }); + "Front Right Motor Speed", [&] { return m_frontRightOutput; }, + m_frontRightMotor); builder.AddDoubleProperty( - "Rear Left Motor Speed", [=, this] { return m_rearLeftMotor->Get(); }, - [=, this](double value) { m_rearLeftMotor->Set(value); }); + "Rear Left Motor Speed", [&] { return m_rearLeftOutput; }, + m_rearLeftMotor); builder.AddDoubleProperty( - "Rear Right Motor Speed", [=, this] { return m_rearRightMotor->Get(); }, - [=, this](double value) { m_rearRightMotor->Set(value); }); + "Rear Right Motor Speed", [&] { return m_rearRightOutput; }, + m_rearRightMotor); } diff --git a/wpilibc/src/main/native/cpp/event/EventLoop.cpp b/wpilibc/src/main/native/cpp/event/EventLoop.cpp index 5af79c96d36..c85286ba371 100644 --- a/wpilibc/src/main/native/cpp/event/EventLoop.cpp +++ b/wpilibc/src/main/native/cpp/event/EventLoop.cpp @@ -4,20 +4,41 @@ #include "frc/event/EventLoop.h" +#include "frc/Errors.h" + using namespace frc; +namespace { +struct RunningSetter { + bool& m_running; + explicit RunningSetter(bool& running) noexcept : m_running{running} { + m_running = true; + } + ~RunningSetter() noexcept { m_running = false; } +}; +} // namespace + EventLoop::EventLoop() {} void EventLoop::Bind(wpi::unique_function action) { + if (m_running) { + throw FRC_MakeError(err::Error, + "Cannot bind EventLoop while it is running"); + } m_bindings.emplace_back(std::move(action)); } void EventLoop::Poll() { + RunningSetter runSetter{m_running}; for (wpi::unique_function& action : m_bindings) { action(); } } void EventLoop::Clear() { + if (m_running) { + throw FRC_MakeError(err::Error, + "Cannot clear EventLoop while it is running"); + } m_bindings.clear(); } diff --git a/wpilibc/src/main/native/cpp/internal/DriverStationModeThread.cpp b/wpilibc/src/main/native/cpp/internal/DriverStationModeThread.cpp index 40856580f06..78d580e36c0 100644 --- a/wpilibc/src/main/native/cpp/internal/DriverStationModeThread.cpp +++ b/wpilibc/src/main/native/cpp/internal/DriverStationModeThread.cpp @@ -23,13 +23,14 @@ DriverStationModeThread::~DriverStationModeThread() { } } -void DriverStationModeThread::InAutonomous(bool entering) { - m_userInAutonomous = entering; -} void DriverStationModeThread::InDisabled(bool entering) { m_userInDisabled = entering; } +void DriverStationModeThread::InAutonomous(bool entering) { + m_userInAutonomous = entering; +} + void DriverStationModeThread::InTeleop(bool entering) { m_userInTeleop = entering; } diff --git a/wpilibc/src/main/native/cpp/motorcontrol/MotorControllerGroup.cpp b/wpilibc/src/main/native/cpp/motorcontrol/MotorControllerGroup.cpp index f855d14b0ee..cf57f7c55b2 100644 --- a/wpilibc/src/main/native/cpp/motorcontrol/MotorControllerGroup.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/MotorControllerGroup.cpp @@ -12,6 +12,8 @@ using namespace frc; // Can't use a delegated constructor here because of an MSVC bug. // https://developercommunity.visualstudio.com/content/problem/583/compiler-bug-with-delegating-a-constructor.html +WPI_IGNORE_DEPRECATED + MotorControllerGroup::MotorControllerGroup( std::vector>&& motorControllers) : m_motorControllers(std::move(motorControllers)) { @@ -74,3 +76,5 @@ void MotorControllerGroup::InitSendable(wpi::SendableBuilder& builder) { "Value", [=, this] { return Get(); }, [=, this](double value) { Set(value); }); } + +WPI_UNIGNORE_DEPRECATED diff --git a/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp b/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp index f25aa9180bf..a05b7cc8416 100644 --- a/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp @@ -11,6 +11,8 @@ using namespace frc; +WPI_IGNORE_DEPRECATED + NidecBrushless::NidecBrushless(int pwmChannel, int dioChannel) : m_dio(dioChannel), m_pwm(pwmChannel) { wpi::SendableRegistry::AddChild(this, &m_dio); @@ -26,6 +28,8 @@ NidecBrushless::NidecBrushless(int pwmChannel, int dioChannel) wpi::SendableRegistry::AddLW(this, "Nidec Brushless", pwmChannel); } +WPI_UNIGNORE_DEPRECATED + void NidecBrushless::Set(double speed) { if (!m_disabled) { m_speed = speed; diff --git a/wpilibc/src/main/native/cpp/motorcontrol/PWMMotorController.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMMotorController.cpp index 3692f757b12..924e4fd3053 100644 --- a/wpilibc/src/main/native/cpp/motorcontrol/PWMMotorController.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMMotorController.cpp @@ -8,13 +8,31 @@ #include #include +#include "frc/RobotController.h" + using namespace frc; void PWMMotorController::Set(double speed) { - m_pwm.SetSpeed(m_isInverted ? -speed : speed); + if (m_isInverted) { + speed = -speed; + } + m_pwm.SetSpeed(speed); + + for (auto& follower : m_nonowningFollowers) { + follower->Set(speed); + } + for (auto& follower : m_owningFollowers) { + follower->Set(speed); + } + Feed(); } +void PWMMotorController::SetVoltage(units::volt_t output) { + // NOLINTNEXTLINE(bugprone-integer-division) + Set(output / RobotController::GetBatteryVoltage()); +} + double PWMMotorController::Get() const { return m_pwm.GetSpeed() * (m_isInverted ? -1.0 : 1.0); } @@ -48,11 +66,19 @@ void PWMMotorController::EnableDeadbandElimination(bool eliminateDeadband) { m_pwm.EnableDeadbandElimination(eliminateDeadband); } +void PWMMotorController::AddFollower(PWMMotorController& follower) { + m_nonowningFollowers.emplace_back(&follower); +} + +WPI_IGNORE_DEPRECATED + PWMMotorController::PWMMotorController(std::string_view name, int channel) : m_pwm(channel, false) { wpi::SendableRegistry::AddLW(this, name, channel); } +WPI_UNIGNORE_DEPRECATED + void PWMMotorController::InitSendable(wpi::SendableBuilder& builder) { builder.SetSmartDashboardType("Motor Controller"); builder.SetActuator(true); diff --git a/wpilibc/src/main/native/cpp/simulation/PneumaticsBaseSim.cpp b/wpilibc/src/main/native/cpp/simulation/PneumaticsBaseSim.cpp index 476d07ae735..04aef3a6699 100644 --- a/wpilibc/src/main/native/cpp/simulation/PneumaticsBaseSim.cpp +++ b/wpilibc/src/main/native/cpp/simulation/PneumaticsBaseSim.cpp @@ -12,11 +12,6 @@ using namespace frc; using namespace frc::sim; -PneumaticsBaseSim::PneumaticsBaseSim(int module) : m_index{module} {} - -PneumaticsBaseSim::PneumaticsBaseSim(const PneumaticsBase& module) - : m_index{module.GetModuleNumber()} {} - std::shared_ptr PneumaticsBaseSim::GetForType( int module, PneumaticsModuleType type) { switch (type) { @@ -31,3 +26,8 @@ std::shared_ptr PneumaticsBaseSim::GetForType( static_cast(module)); } } + +PneumaticsBaseSim::PneumaticsBaseSim(int module) : m_index{module} {} + +PneumaticsBaseSim::PneumaticsBaseSim(const PneumaticsBase& module) + : m_index{module.GetModuleNumber()} {} diff --git a/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp b/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp index 30a565039c0..1016c4bccbc 100644 --- a/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp +++ b/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp @@ -18,8 +18,8 @@ UltrasonicSim::UltrasonicSim(int ping, int echo) { m_simRange = deviceSim.GetDouble("Range (in)"); } -void UltrasonicSim::SetRangeValid(bool isValid) { - m_simRangeValid.Set(isValid); +void UltrasonicSim::SetRangeValid(bool valid) { + m_simRangeValid.Set(valid); } void UltrasonicSim::SetRange(units::inch_t range) { diff --git a/wpilibc/src/main/native/cpp/smartdashboard/SmartDashboard.cpp b/wpilibc/src/main/native/cpp/smartdashboard/SmartDashboard.cpp index 65ca59799e9..d57473fbe04 100644 --- a/wpilibc/src/main/native/cpp/smartdashboard/SmartDashboard.cpp +++ b/wpilibc/src/main/native/cpp/smartdashboard/SmartDashboard.cpp @@ -72,7 +72,8 @@ bool SmartDashboard::IsPersistent(std::string_view key) { nt::NetworkTableEntry SmartDashboard::GetEntry(std::string_view key) { if (!gReported) { - HAL_Report(HALUsageReporting::kResourceType_SmartDashboard, 0); + HAL_Report(HALUsageReporting::kResourceType_SmartDashboard, + HALUsageReporting::kSmartDashboard_Instance); gReported = true; } return GetInstance().table->GetEntry(key); @@ -83,7 +84,8 @@ void SmartDashboard::PutData(std::string_view key, wpi::Sendable* data) { throw FRC_MakeError(err::NullParameter, "value"); } if (!gReported) { - HAL_Report(HALUsageReporting::kResourceType_SmartDashboard, 0); + HAL_Report(HALUsageReporting::kResourceType_SmartDashboard, + HALUsageReporting::kSmartDashboard_Instance); gReported = true; } auto& inst = GetInstance(); diff --git a/wpilibc/src/main/native/cpp/sysid/SysIdRoutineLog.cpp b/wpilibc/src/main/native/cpp/sysid/SysIdRoutineLog.cpp new file mode 100644 index 00000000000..1ee35b8863b --- /dev/null +++ b/wpilibc/src/main/native/cpp/sysid/SysIdRoutineLog.cpp @@ -0,0 +1,67 @@ +// 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 "frc/sysid/SysIdRoutineLog.h" + +#include + +#include "frc/DataLogManager.h" + +using namespace frc::sysid; + +SysIdRoutineLog::SysIdRoutineLog(std::string_view logName) + : m_logName(logName) {} + +SysIdRoutineLog::MotorLog::MotorLog(std::string_view motorName, + std::string_view logName, + LogEntries* logEntries) + : m_motorName(motorName), m_logName(logName), m_logEntries(logEntries) { + (*logEntries)[motorName] = MotorEntries(); +} + +SysIdRoutineLog::MotorLog& SysIdRoutineLog::MotorLog::value( + std::string_view name, double value, std::string_view unit) { + auto& motorEntries = (*m_logEntries)[m_motorName]; + + if (!motorEntries.contains(name)) { + wpi::log::DataLog& log = frc::DataLogManager::GetLog(); + + motorEntries[name] = wpi::log::DoubleLogEntry( + log, fmt::format("{}-{}-{}", name, m_motorName, m_logName), unit); + } + + motorEntries[name].Append(value); + return *this; +} + +SysIdRoutineLog::MotorLog SysIdRoutineLog::Motor(std::string_view motorName) { + return MotorLog{motorName, m_logName, &m_logEntries}; +} + +void SysIdRoutineLog::RecordState(State state) { + if (!m_stateInitialized) { + m_state = + wpi::log::StringLogEntry{frc::DataLogManager::GetLog(), + fmt::format("sysid-test-state-{}", m_logName)}; + m_stateInitialized = true; + } + m_state.Append(StateEnumToString(state)); +} + +std::string SysIdRoutineLog::StateEnumToString(State state) { + switch (state) { + case State::kQuasistaticForward: + return "quasistatic-forward"; + case State::kQuasistaticReverse: + return "quasistatic-reverse"; + case State::kDynamicForward: + return "dynamic-forward"; + case State::kDynamicReverse: + return "dynamic-reverse"; + case State::kNone: + return "none"; + default: + return "none"; + } +} diff --git a/wpilibc/src/main/native/include/frc/ADIS16448_IMU.h b/wpilibc/src/main/native/include/frc/ADIS16448_IMU.h index 3f4a1c2ebda..0b16782b8ab 100644 --- a/wpilibc/src/main/native/include/frc/ADIS16448_IMU.h +++ b/wpilibc/src/main/native/include/frc/ADIS16448_IMU.h @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -33,7 +32,6 @@ #include "frc/DigitalInput.h" #include "frc/DigitalOutput.h" -#include "frc/DigitalSource.h" #include "frc/SPI.h" namespace frc { @@ -56,23 +54,47 @@ namespace frc { class ADIS16448_IMU : public wpi::Sendable, public wpi::SendableHelper { public: - /* ADIS16448 Calibration Time Enum Class */ + /** + * ADIS16448 calibration times. + */ enum class CalibrationTime { + /// 32 ms calibration time. _32ms = 0, + /// 64 ms calibration time. _64ms = 1, + /// 128 ms calibration time. _128ms = 2, + /// 256 ms calibration time. _256ms = 3, + /// 512 ms calibration time. _512ms = 4, + /// 1 s calibration time. _1s = 5, + /// 2 s calibration time. _2s = 6, + /// 4 s calibration time. _4s = 7, + /// 8 s calibration time. _8s = 8, + /// 16 s calibration time. _16s = 9, + /// 32 s calibration time. _32s = 10, + /// 64 s calibration time. _64s = 11 }; - enum IMUAxis { kX, kY, kZ }; + /** + * IMU axes. + */ + enum IMUAxis { + /// The IMU's X axis. + kX, + /// The IMU's Y axis. + kY, + /// The IMU's Z axis. + kZ + }; /** * IMU constructor on onboard MXP CS0, Z-up orientation, and complementary @@ -93,8 +115,8 @@ class ADIS16448_IMU : public wpi::Sendable, ~ADIS16448_IMU() override; - ADIS16448_IMU(ADIS16448_IMU&&) = default; - ADIS16448_IMU& operator=(ADIS16448_IMU&&) = default; + ADIS16448_IMU(ADIS16448_IMU&&); + ADIS16448_IMU& operator=(ADIS16448_IMU&&); /** * Initialize the IMU. @@ -183,31 +205,71 @@ class ADIS16448_IMU : public wpi::Sendable, */ units::meters_per_second_squared_t GetAccelZ() const; + /** + * Returns the complementary angle around the X axis computed from + * accelerometer and gyro rate measurements. + */ units::degree_t GetXComplementaryAngle() const; + /** + * Returns the complementary angle around the Y axis computed from + * accelerometer and gyro rate measurements. + */ units::degree_t GetYComplementaryAngle() const; + /** + * Returns the X-axis filtered acceleration angle. + */ units::degree_t GetXFilteredAccelAngle() const; + /** + * Returns the Y-axis filtered acceleration angle. + */ units::degree_t GetYFilteredAccelAngle() const; + /** + * Returns the magnetic field strength in the X axis. + */ units::tesla_t GetMagneticFieldX() const; + /** + * Returns the magnetic field strength in the Y axis. + */ units::tesla_t GetMagneticFieldY() const; + /** + * Returns the magnetic field strength in the Z axis. + */ units::tesla_t GetMagneticFieldZ() const; + /** + * Returns the barometric pressure. + */ units::pounds_per_square_inch_t GetBarometricPressure() const; + /** + * Returns the temperature. + */ units::celsius_t GetTemperature() const; IMUAxis GetYawAxis() const; int SetYawAxis(IMUAxis yaw_axis); + /** + * Checks the connection status of the IMU. + * + * @return True if the IMU is connected, false otherwise. + */ bool IsConnected() const; - int ConfigDecRate(uint16_t DecimationRate); + /** + * Configures the decimation rate of the IMU. + * + * @param decimationRate The new decimation value. + * @return 0 if success, 1 if no change, 2 if error. + */ + int ConfigDecRate(uint16_t decimationRate); /** * Get the SPI port number. @@ -288,8 +350,8 @@ class ADIS16448_IMU : public wpi::Sendable, }; /** @brief Internal Resources **/ - DigitalInput* m_reset_in; - DigitalOutput* m_status_led; + DigitalInput* m_reset_in = nullptr; + DigitalOutput* m_status_led = nullptr; bool SwitchToStandardSPI(); @@ -353,10 +415,10 @@ class ADIS16448_IMU : public wpi::Sendable, double CompFilterProcess(double compAngle, double accelAngle, double omega); // State and resource variables - volatile bool m_thread_active = false; - volatile bool m_first_run = true; - volatile bool m_thread_idle = false; - volatile bool m_start_up_mode = true; + std::atomic m_thread_active = false; + std::atomic m_first_run = true; + std::atomic m_thread_idle = false; + std::atomic m_start_up_mode = true; bool m_auto_configured = false; SPI::Port m_spi_port; diff --git a/wpilibc/src/main/native/include/frc/ADIS16470_IMU.h b/wpilibc/src/main/native/include/frc/ADIS16470_IMU.h index ad8baa0e048..0c030c7a9e3 100644 --- a/wpilibc/src/main/native/include/frc/ADIS16470_IMU.h +++ b/wpilibc/src/main/native/include/frc/ADIS16470_IMU.h @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -30,7 +29,6 @@ #include "frc/DigitalInput.h" #include "frc/DigitalOutput.h" -#include "frc/DigitalSource.h" #include "frc/SPI.h" namespace frc { @@ -53,40 +51,109 @@ namespace frc { class ADIS16470_IMU : public wpi::Sendable, public wpi::SendableHelper { public: - /* ADIS16470 Calibration Time Enum Class */ + /** + * ADIS16470 calibration times. + */ enum class CalibrationTime { + /// 32 ms calibration time. _32ms = 0, + /// 64 ms calibration time. _64ms = 1, + /// 128 ms calibration time. _128ms = 2, + /// 256 ms calibration time. _256ms = 3, + /// 512 ms calibration time. _512ms = 4, + /// 1 s calibration time. _1s = 5, + /// 2 s calibration time. _2s = 6, + /// 4 s calibration time. _4s = 7, + /// 8 s calibration time. _8s = 8, + /// 16 s calibration time. _16s = 9, + /// 32 s calibration time. _32s = 10, + /// 64 s calibration time. _64s = 11 }; - enum IMUAxis { kX, kY, kZ, kYaw, kPitch, kRoll }; + /** + * IMU axes. + * + * kX, kY, and kZ refer to the IMU's X, Y, and Z axes respectively. kYaw, + * kPitch, and kRoll are configured by the user to refer to an X, Y, or Z + * axis. + */ + enum IMUAxis { + /// The IMU's X axis. + kX, + /// The IMU's Y axis. + kY, + /// The IMU's Z axis. + kZ, + /// The user-configured yaw axis. + kYaw, + /// The user-configured pitch axis. + kPitch, + /// The user-configured roll axis. + kRoll + }; + /** + * Creates a new ADIS16740 IMU object. + * + * The default setup is the onboard SPI port with a calibration time of 4 + * seconds. Yaw, pitch, and roll are kZ, kX, and kY respectively. + */ ADIS16470_IMU(); + + /** + * Creates a new ADIS16740 IMU object. + * + * The default setup is the onboard SPI port with a calibration time of 4 + * seconds. + * + * Input axes limited to kX, kY and kZ. Specifying kYaw, kPitch,or kRoll + * will result in an error. + * + * @param yaw_axis The axis that measures the yaw + * @param pitch_axis The axis that measures the pitch + * @param roll_axis The axis that measures the roll + */ ADIS16470_IMU(IMUAxis yaw_axis, IMUAxis pitch_axis, IMUAxis roll_axis); + /** + * Creates a new ADIS16740 IMU object. + * + * Input axes limited to kX, kY and kZ. Specifying kYaw, kPitch, or + * kRoll will result in an error. + * + * @param yaw_axis The axis that measures the yaw + * @param pitch_axis The axis that measures the pitch + * @param roll_axis The axis that measures the roll + * @param port The SPI Port the gyro is plugged into + * @param cal_time Calibration time + */ explicit ADIS16470_IMU(IMUAxis yaw_axis, IMUAxis pitch_axis, IMUAxis roll_axis, frc::SPI::Port port, CalibrationTime cal_time); ~ADIS16470_IMU() override; - ADIS16470_IMU(ADIS16470_IMU&&) = default; - ADIS16470_IMU& operator=(ADIS16470_IMU&&) = default; + ADIS16470_IMU(ADIS16470_IMU&& other); + ADIS16470_IMU& operator=(ADIS16470_IMU&& other); /** - * @brief Configures the decimation rate of the IMU. + * Configures the decimation rate of the IMU. + * + * @param decimationRate The new decimation value. + * @return 0 if success, 1 if no change, 2 if error. */ - int ConfigDecRate(uint16_t reg); + int ConfigDecRate(uint16_t decimationRate); /** * @brief Switches the active SPI port to standard SPI mode, writes the @@ -101,8 +168,11 @@ class ADIS16470_IMU : public wpi::Sendable, int ConfigCalTime(CalibrationTime new_cal_time); /** - * @brief Resets the gyro accumulations to a heading of zero. This can be used - * if the "zero" orientation of the sensor needs to be changed in runtime. + * Reset the gyro. + * + * Resets the gyro accumulations to a heading of zero. This can be used if + * there is significant drift in the gyro and it needs to be recalibrated + * after running. */ void Reset(); @@ -144,16 +214,22 @@ class ADIS16470_IMU : public wpi::Sendable, void SetGyroAngleZ(units::degree_t angle); /** - * @param axis The IMUAxis whose angle to return - * @return The axis angle (CCW positive) + * Returns the axis angle (CCW positive). + * + * @param axis The IMUAxis whose angle to return. Defaults to user configured + * Yaw. + * @return The axis angle (CCW positive). */ - units::degree_t GetAngle(IMUAxis axis) const; + units::degree_t GetAngle(IMUAxis axis = IMUAxis::kYaw) const; /** - * @param axis The IMUAxis whose rate to return - * @return Axis angular rate (CCW positive) + * Returns the axis angular rate (CCW positive). + * + * @param axis The IMUAxis whose rate to return. Defaults to user configured + * Yaw. + * @return Axis angular rate (CCW positive). */ - units::degrees_per_second_t GetRate(IMUAxis axis) const; + units::degrees_per_second_t GetRate(IMUAxis axis = IMUAxis::kYaw) const; /** * Returns the acceleration in the X axis. @@ -347,8 +423,8 @@ class ADIS16470_IMU : public wpi::Sendable, static constexpr double grav = 9.81; /** @brief Resources **/ - DigitalInput* m_reset_in; - DigitalOutput* m_status_led; + DigitalInput* m_reset_in = nullptr; + DigitalOutput* m_status_led = nullptr; /** * @brief Switches to standard SPI operation. Primarily used when exiting auto @@ -425,9 +501,9 @@ class ADIS16470_IMU : public wpi::Sendable, double CompFilterProcess(double compAngle, double accelAngle, double omega); // State and resource variables - volatile bool m_thread_active = false; - volatile bool m_first_run = true; - volatile bool m_thread_idle = false; + std::atomic m_thread_active = false; + std::atomic m_first_run = true; + std::atomic m_thread_idle = false; bool m_auto_configured = false; SPI::Port m_spi_port; uint16_t m_calibration_time = 0; diff --git a/wpilibc/src/main/native/include/frc/ADXL345_I2C.h b/wpilibc/src/main/native/include/frc/ADXL345_I2C.h index 2fe0da4a350..4c6d9dffe2e 100644 --- a/wpilibc/src/main/native/include/frc/ADXL345_I2C.h +++ b/wpilibc/src/main/native/include/frc/ADXL345_I2C.h @@ -26,16 +26,47 @@ namespace frc { class ADXL345_I2C : public nt::NTSendable, public wpi::SendableHelper { public: - enum Range { kRange_2G = 0, kRange_4G = 1, kRange_8G = 2, kRange_16G = 3 }; + /** + * Accelerometer range. + */ + enum Range { + /// 2 Gs max. + kRange_2G = 0, + /// 4 Gs max. + kRange_4G = 1, + /// 8 Gs max. + kRange_8G = 2, + /// 16 Gs max. + kRange_16G = 3 + }; - enum Axes { kAxis_X = 0x00, kAxis_Y = 0x02, kAxis_Z = 0x04 }; + /** + * Accelerometer axes. + */ + enum Axes { + /// X axis. + kAxis_X = 0x00, + /// Y axis. + kAxis_Y = 0x02, + /// Z axis. + kAxis_Z = 0x04 + }; + /** + * Container type for accelerations from all axes. + */ struct AllAxes { - double XAxis; - double YAxis; - double ZAxis; + /// Acceleration along the X axis in g-forces. + double XAxis = 0.0; + /// Acceleration along the Y axis in g-forces. + double YAxis = 0.0; + /// Acceleration along the Z axis in g-forces. + double ZAxis = 0.0; }; + /// Default I2C device address. + static constexpr int kAddress = 0x1D; + /** * Constructs the ADXL345 Accelerometer over I2C. * @@ -100,7 +131,7 @@ class ADXL345_I2C : public nt::NTSendable, void InitSendable(nt::NTSendableBuilder& builder) override; - protected: + private: I2C m_i2c; hal::SimDevice m_simDevice; @@ -109,7 +140,6 @@ class ADXL345_I2C : public nt::NTSendable, hal::SimDouble m_simY; hal::SimDouble m_simZ; - static constexpr int kAddress = 0x1D; static constexpr int kPowerCtlRegister = 0x2D; static constexpr int kDataFormatRegister = 0x31; static constexpr int kDataRegister = 0x32; diff --git a/wpilibc/src/main/native/include/frc/ADXL345_SPI.h b/wpilibc/src/main/native/include/frc/ADXL345_SPI.h index 3f2d8aebab4..305be9280a2 100644 --- a/wpilibc/src/main/native/include/frc/ADXL345_SPI.h +++ b/wpilibc/src/main/native/include/frc/ADXL345_SPI.h @@ -21,14 +21,42 @@ namespace frc { class ADXL345_SPI : public nt::NTSendable, public wpi::SendableHelper { public: - enum Range { kRange_2G = 0, kRange_4G = 1, kRange_8G = 2, kRange_16G = 3 }; + /** + * Accelerometer range. + */ + enum Range { + /// 2 Gs max. + kRange_2G = 0, + /// 4 Gs max. + kRange_4G = 1, + /// 8 Gs max. + kRange_8G = 2, + /// 16 Gs max. + kRange_16G = 3 + }; - enum Axes { kAxis_X = 0x00, kAxis_Y = 0x02, kAxis_Z = 0x04 }; + /** + * Accelerometer axes. + */ + enum Axes { + /// X axis. + kAxis_X = 0x00, + /// Y axis. + kAxis_Y = 0x02, + /// Z axis. + kAxis_Z = 0x04 + }; + /** + * Container type for accelerations from all axes. + */ struct AllAxes { - double XAxis; - double YAxis; - double ZAxis; + /// Acceleration along the X axis in g-forces. + double XAxis = 0.0; + /// Acceleration along the Y axis in g-forces. + double YAxis = 0.0; + /// Acceleration along the Z axis in g-forces. + double ZAxis = 0.0; }; /** @@ -93,7 +121,7 @@ class ADXL345_SPI : public nt::NTSendable, void InitSendable(nt::NTSendableBuilder& builder) override; - protected: + private: SPI m_spi; hal::SimDevice m_simDevice; diff --git a/wpilibc/src/main/native/include/frc/ADXL362.h b/wpilibc/src/main/native/include/frc/ADXL362.h index ddf6ebed1a2..0332b3103fe 100644 --- a/wpilibc/src/main/native/include/frc/ADXL362.h +++ b/wpilibc/src/main/native/include/frc/ADXL362.h @@ -19,13 +19,40 @@ namespace frc { */ class ADXL362 : public nt::NTSendable, public wpi::SendableHelper { public: - enum Range { kRange_2G = 0, kRange_4G = 1, kRange_8G = 2 }; + /** + * Accelerometer range. + */ + enum Range { + /// 2 Gs max. + kRange_2G = 0, + /// 4 Gs max. + kRange_4G = 1, + /// 8 Gs max. + kRange_8G = 2 + }; - enum Axes { kAxis_X = 0x00, kAxis_Y = 0x02, kAxis_Z = 0x04 }; + /** + * Accelerometer axes. + */ + enum Axes { + /// X axis. + kAxis_X = 0x00, + /// Y axis. + kAxis_Y = 0x02, + /// Z axis. + kAxis_Z = 0x04 + }; + + /** + * Container type for accelerations from all axes. + */ struct AllAxes { - double XAxis; - double YAxis; - double ZAxis; + /// Acceleration along the X axis in g-forces. + double XAxis = 0.0; + /// Acceleration along the Y axis in g-forces. + double YAxis = 0.0; + /// Acceleration along the Z axis in g-forces. + double ZAxis = 0.0; }; public: diff --git a/wpilibc/src/main/native/include/frc/AnalogGyro.h b/wpilibc/src/main/native/include/frc/AnalogGyro.h index 0c472fbf7f8..3913b404884 100644 --- a/wpilibc/src/main/native/include/frc/AnalogGyro.h +++ b/wpilibc/src/main/native/include/frc/AnalogGyro.h @@ -218,10 +218,8 @@ class AnalogGyro : public wpi::Sendable, void InitSendable(wpi::SendableBuilder& builder) override; - protected: - std::shared_ptr m_analog; - private: + std::shared_ptr m_analog; hal::Handle m_gyroHandle; }; diff --git a/wpilibc/src/main/native/include/frc/AnalogOutput.h b/wpilibc/src/main/native/include/frc/AnalogOutput.h index ccb3c9c91fe..4151ae67557 100644 --- a/wpilibc/src/main/native/include/frc/AnalogOutput.h +++ b/wpilibc/src/main/native/include/frc/AnalogOutput.h @@ -33,14 +33,14 @@ class AnalogOutput : public wpi::Sendable, /** * Set the value of the analog output. * - * @param voltage The output value in Volts, from 0.0 to +5.0 + * @param voltage The output value in Volts, from 0.0 to +5.0. */ void SetVoltage(double voltage); /** - * Get the voltage of the analog output + * Get the voltage of the analog output. * - * @return The value in Volts, from 0.0 to +5.0 + * @return The value in Volts, from 0.0 to +5.0. */ double GetVoltage() const; diff --git a/wpilibc/src/main/native/include/frc/AnalogTriggerType.h b/wpilibc/src/main/native/include/frc/AnalogTriggerType.h index c706c344337..0db114d598a 100644 --- a/wpilibc/src/main/native/include/frc/AnalogTriggerType.h +++ b/wpilibc/src/main/native/include/frc/AnalogTriggerType.h @@ -6,10 +6,15 @@ namespace frc { +/** Defines the state in which the AnalogTrigger triggers. */ enum class AnalogTriggerType { + /// In window. kInWindow = 0, + /// State. kState = 1, + /// Rising Pulse. kRisingPulse = 2, + /// Falling pulse. kFallingPulse = 3 }; diff --git a/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h b/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h index a8f0adc4deb..270ed80d55c 100644 --- a/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h +++ b/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h @@ -17,7 +17,17 @@ namespace frc { class BuiltInAccelerometer : public wpi::Sendable, public wpi::SendableHelper { public: - enum Range { kRange_2G = 0, kRange_4G = 1, kRange_8G = 2 }; + /** + * Accelerometer range. + */ + enum Range { + /// 2 Gs max. + kRange_2G = 0, + /// 4 Gs max. + kRange_4G = 1, + /// 8 Gs max. + kRange_8G = 2 + }; /** * Constructor. diff --git a/wpilibc/src/main/native/include/frc/CAN.h b/wpilibc/src/main/native/include/frc/CAN.h index dc04988f1f3..1f5d1682a0f 100644 --- a/wpilibc/src/main/native/include/frc/CAN.h +++ b/wpilibc/src/main/native/include/frc/CAN.h @@ -170,7 +170,10 @@ class CAN { */ static uint64_t GetTimestampBaseTime(); + /// Team manufacturer. static constexpr HAL_CANManufacturer kTeamManufacturer = HAL_CAN_Man_kTeamUse; + + /// Team device type. static constexpr HAL_CANDeviceType kTeamDeviceType = HAL_CAN_Dev_kMiscellaneous; diff --git a/wpilibc/src/main/native/include/frc/CompressorConfigType.h b/wpilibc/src/main/native/include/frc/CompressorConfigType.h index 5a5a1c13a1f..b9338042b6b 100644 --- a/wpilibc/src/main/native/include/frc/CompressorConfigType.h +++ b/wpilibc/src/main/native/include/frc/CompressorConfigType.h @@ -5,10 +5,17 @@ #pragma once namespace frc { +/** + * Compressor config type. + */ enum class CompressorConfigType { + /// Disabled. Disabled = 0, + /// Digital. Digital = 1, + /// Analog. Analog = 2, + /// Hybrid. Hybrid = 3 }; diff --git a/wpilibc/src/main/native/include/frc/Counter.h b/wpilibc/src/main/native/include/frc/Counter.h index 71eebcab5a3..3720dde31b3 100644 --- a/wpilibc/src/main/native/include/frc/Counter.h +++ b/wpilibc/src/main/native/include/frc/Counter.h @@ -451,17 +451,20 @@ class Counter : public CounterBase, void InitSendable(wpi::SendableBuilder& builder) override; protected: - // Makes the counter count up. + /// Makes the counter count up. std::shared_ptr m_upSource; - // Makes the counter count down. + /// Makes the counter count down. std::shared_ptr m_downSource; - // The FPGA counter object + /// The FPGA counter object hal::Handle m_counter; private: - int m_index = 0; // The index of this counter. + /// The index of this counter. + int m_index = 0; + + /// Distance of travel for each tick. double m_distancePerPulse = 1; friend class DigitalGlitchFilter; diff --git a/wpilibc/src/main/native/include/frc/DMA.h b/wpilibc/src/main/native/include/frc/DMA.h index 1bbf268f774..d09368fb37f 100644 --- a/wpilibc/src/main/native/include/frc/DMA.h +++ b/wpilibc/src/main/native/include/frc/DMA.h @@ -17,6 +17,9 @@ class DMASample; class PWM; class PWMMotorController; +/** + * Class for configuring Direct Memory Access (DMA) of FPGA inputs. + */ class DMA { friend class DMASample; @@ -27,32 +30,162 @@ class DMA { DMA& operator=(DMA&& other) = default; DMA(DMA&& other) = default; + /** + * Sets whether DMA is paused. + * + * @param pause True pauses DMA. + */ void SetPause(bool pause); - void SetTimedTrigger(units::second_t seconds); + + /** + * Sets DMA to trigger at an interval. + * + * @param period Period at which to trigger DMA. + */ + void SetTimedTrigger(units::second_t period); + + /** + * Sets number of DMA cycles to trigger. + * + * @param cycles Number of cycles. + */ void SetTimedTriggerCycles(int cycles); + /** + * Adds position data for an encoder to be collected by DMA. + * + * This can only be called if DMA is not started. + * + * @param encoder Encoder to add to DMA. + */ void AddEncoder(const Encoder* encoder); + + /** + * Adds timer data for an encoder to be collected by DMA. + * + * This can only be called if DMA is not started. + * + * @param encoder Encoder to add to DMA. + */ void AddEncoderPeriod(const Encoder* encoder); + /** + * Adds position data for an counter to be collected by DMA. + * + * This can only be called if DMA is not started. + * + * @param counter Counter to add to DMA. + */ void AddCounter(const Counter* counter); + + /** + * Adds timer data for an counter to be collected by DMA. + * + * This can only be called if DMA is not started. + * + * @param counter Counter to add to DMA. + */ void AddCounterPeriod(const Counter* counter); + /** + * Adds a digital source to be collected by DMA. + * + * This can only be called if DMA is not started. + * + * @param digitalSource DigitalSource to add to DMA. + */ void AddDigitalSource(const DigitalSource* digitalSource); + /** + * Adds a digital source to be collected by DMA. + * + * This can only be called if DMA is not started. + * + * @param digitalSource DigitalSource to add to DMA. + */ void AddDutyCycle(const DutyCycle* digitalSource); + /** + * Adds an analog input to be collected by DMA. + * + * This can only be called if DMA is not started. + * + * @param analogInput AnalogInput to add to DMA. + */ void AddAnalogInput(const AnalogInput* analogInput); + + /** + * Adds averaged data of an analog input to be collected by DMA. + * + * This can only be called if DMA is not started. + * + * @param analogInput AnalogInput to add to DMA. + */ void AddAveragedAnalogInput(const AnalogInput* analogInput); + + /** + * Adds accumulator data of an analog input to be collected by DMA. + * + * This can only be called if DMA is not started. + * + * @param analogInput AnalogInput to add to DMA. + */ void AddAnalogAccumulator(const AnalogInput* analogInput); + /** + * Sets an external DMA trigger. + * + * @param source the source to trigger from. + * @param rising trigger on rising edge. + * @param falling trigger on falling edge. + * @return the index of the trigger + */ int SetExternalTrigger(DigitalSource* source, bool rising, bool falling); + + /** + * Sets a DMA PWM edge trigger. + * + * @param pwm the PWM to trigger from. + * @param rising trigger on rising edge. + * @param falling trigger on falling edge. + * @return the index of the trigger + */ int SetPwmEdgeTrigger(PWM* pwm, bool rising, bool falling); + + /** + * Sets a DMA PWMMotorController edge trigger. + * + * @param pwm the PWMMotorController to trigger from. + * @param rising trigger on rising edge. + * @param falling trigger on falling edge. + * @return the index of the trigger + */ int SetPwmEdgeTrigger(PWMMotorController* pwm, bool rising, bool falling); + /** + * Clear all sensors from the DMA collection list. + * + * This can only be called if DMA is not started. + */ void ClearSensors(); + + /** + * Clear all external triggers from the DMA trigger list. + * + * This can only be called if DMA is not started. + */ void ClearExternalTriggers(); + /** + * Starts DMA Collection. + * + * @param queueDepth The number of objects to be able to queue. + */ void Start(int queueDepth); + + /** + * Stops DMA Collection. + */ void Stop(); private: diff --git a/wpilibc/src/main/native/include/frc/DMASample.h b/wpilibc/src/main/native/include/frc/DMASample.h index 48a0a9e9cac..b2bed41fc75 100644 --- a/wpilibc/src/main/native/include/frc/DMASample.h +++ b/wpilibc/src/main/native/include/frc/DMASample.h @@ -17,30 +17,71 @@ #include "frc/Encoder.h" namespace frc { +/** + * DMA sample. + */ class DMASample : public HAL_DMASample { public: + /** + * DMA read status. + */ enum class DMAReadStatus { + /// OK status. kOk = HAL_DMA_OK, + /// Timeout status. kTimeout = HAL_DMA_TIMEOUT, + /// Error status. kError = HAL_DMA_ERROR }; + /** + * Retrieves a new DMA sample. + * + * @param dma DMA object. + * @param timeout Timeout for retrieval. + * @param remaining Number of remaining samples. + * @param status DMA read status. + */ DMAReadStatus Update(const DMA* dma, units::second_t timeout, int32_t* remaining, int32_t* status) { return static_cast( HAL_ReadDMA(dma->dmaHandle, this, timeout.value(), remaining, status)); } + /** + * Returns the DMA sample time in microseconds. + * + * @return The DMA sample time in microseconds. + */ uint64_t GetTime() const { return timeStamp; } + /** + * Returns the DMA sample timestamp. + * + * @return The DMA sample timestamp. + */ units::second_t GetTimeStamp() const { return units::second_t{static_cast(GetTime()) * 1.0e-6}; } + /** + * Returns raw encoder value from DMA. + * + * @param encoder Encoder used for DMA. + * @param status DMA read status. + * @return Raw encoder value from DMA. + */ int32_t GetEncoderRaw(const Encoder* encoder, int32_t* status) const { return HAL_GetDMASampleEncoderRaw(this, encoder->m_encoder, status); } + /** + * Returns encoder distance from DMA. + * + * @param encoder Encoder used for DMA. + * @param status DMA read status. + * @return Encoder distance from DMA. + */ double GetEncoderDistance(const Encoder* encoder, int32_t* status) const { double val = GetEncoderRaw(encoder, status); val *= encoder->DecodingScaleFactor(); @@ -48,41 +89,97 @@ class DMASample : public HAL_DMASample { return val; } + /** + * Returns raw encoder period from DMA. + * + * @param encoder Encoder used for DMA. + * @param status DMA read status. + * @return Raw encoder period from DMA. + */ int32_t GetEncoderPeriodRaw(const Encoder* encoder, int32_t* status) const { return HAL_GetDMASampleEncoderPeriodRaw(this, encoder->m_encoder, status); } + /** + * Returns counter value from DMA. + * + * @param counter Counter used for DMA. + * @param status DMA read status. + * @return Counter value from DMA. + */ int32_t GetCounter(const Counter* counter, int32_t* status) const { return HAL_GetDMASampleCounter(this, counter->m_counter, status); } + /** + * Returns counter period from DMA. + * + * @param counter Counter used for DMA. + * @param status DMA read status. + * @return Counter period from DMA. + */ int32_t GetCounterPeriod(const Counter* counter, int32_t* status) const { return HAL_GetDMASampleCounterPeriod(this, counter->m_counter, status); } + /** + * Returns digital source value from DMA. + * + * @param digitalSource DigitalSource used for DMA. + * @param status DMA read status. + * @return DigitalSource value from DMA. + */ bool GetDigitalSource(const DigitalSource* digitalSource, int32_t* status) const { return HAL_GetDMASampleDigitalSource( this, digitalSource->GetPortHandleForRouting(), status); } + /** + * Returns raw analog input value from DMA. + * + * @param analogInput AnalogInput used for DMA. + * @param status DMA read status. + * @return Raw analog input value from DMA. + */ int32_t GetAnalogInputRaw(const AnalogInput* analogInput, int32_t* status) const { return HAL_GetDMASampleAnalogInputRaw(this, analogInput->m_port, status); } + /** + * Returns analog input voltage from DMA. + * + * @param analogInput AnalogInput used for DMA. + * @param status DMA read status. + * @return Analog input voltage from DMA. + */ double GetAnalogInputVoltage(const AnalogInput* analogInput, int32_t* status) { return HAL_GetAnalogValueToVolts( analogInput->m_port, GetAnalogInputRaw(analogInput, status), status); } + /** + * Returns averaged analog input raw value from DMA. + * + * @param analogInput AnalogInput used for DMA. + * @param status DMA read status. + * @return Averaged analog input raw value from DMA. + */ int32_t GetAveragedAnalogInputRaw(const AnalogInput* analogInput, int32_t* status) const { return HAL_GetDMASampleAveragedAnalogInputRaw(this, analogInput->m_port, status); } + /** + * Returns averaged analog input voltage from DMA. + * + * @param analogInput AnalogInput used for DMA. + * @param status DMA read status. + * @return Averaged analog input voltage from DMA. + */ double GetAveragedAnalogInputVoltage(const AnalogInput* analogInput, int32_t* status) { return HAL_GetAnalogValueToVolts( @@ -90,18 +187,40 @@ class DMASample : public HAL_DMASample { status); } + /** + * Returns analog accumulator value from DMA. + * + * @param analogInput AnalogInput used for DMA. + * @param count Accumulator sample count. + * @param value Accumulator value. + * @param status DMA read status. + */ void GetAnalogAccumulator(const AnalogInput* analogInput, int64_t* count, int64_t* value, int32_t* status) const { return HAL_GetDMASampleAnalogAccumulator(this, analogInput->m_port, count, value, status); } + /** + * Returns raw duty cycle output from DMA. + * + * @param dutyCycle DutyCycle used for DMA. + * @param status DMA read status. + * @return Raw duty cycle output from DMA. + */ int32_t GetDutyCycleOutputRaw(const DutyCycle* dutyCycle, int32_t* status) const { return HAL_GetDMASampleDutyCycleOutputRaw(this, dutyCycle->m_handle, status); } + /** + * Returns duty cycle output (0-1) from DMA. + * + * @param dutyCycle DutyCycle used for DMA. + * @param status DMA read status. + * @return Duty cycle output (0-1) from DMA. + */ double GetDutyCycleOutput(const DutyCycle* dutyCycle, int32_t* status) { return GetDutyCycleOutputRaw(dutyCycle, status) / static_cast(dutyCycle->GetOutputScaleFactor()); diff --git a/wpilibc/src/main/native/include/frc/DoubleSolenoid.h b/wpilibc/src/main/native/include/frc/DoubleSolenoid.h index f02ba5a1949..57c2c11d82a 100644 --- a/wpilibc/src/main/native/include/frc/DoubleSolenoid.h +++ b/wpilibc/src/main/native/include/frc/DoubleSolenoid.h @@ -25,7 +25,17 @@ namespace frc { class DoubleSolenoid : public wpi::Sendable, public wpi::SendableHelper { public: - enum Value { kOff, kForward, kReverse }; + /** + * Possible values for a DoubleSolenoid. + */ + enum Value { + /// Off position. + kOff, + /// Forward position. + kForward, + /// Reverse position. + kReverse + }; /** * Constructs a double solenoid for a specified module of a specific module diff --git a/wpilibc/src/main/native/include/frc/DriverStation.h b/wpilibc/src/main/native/include/frc/DriverStation.h index 3b2b3df86da..b9b683f11b4 100644 --- a/wpilibc/src/main/native/include/frc/DriverStation.h +++ b/wpilibc/src/main/native/include/frc/DriverStation.h @@ -22,9 +22,31 @@ namespace frc { */ class DriverStation final { public: - enum Alliance { kRed, kBlue }; - enum MatchType { kNone, kPractice, kQualification, kElimination }; + /** + * The robot alliance that the robot is a part of. + */ + enum Alliance { + /// Red alliance. + kRed, + /// Blue alliance. + kBlue + }; + /** + * The type of robot match that the robot is part of. + */ + enum MatchType { + /// None. + kNone, + /// Practice. + kPractice, + /// Qualification. + kQualification, + /// Elimination. + kElimination + }; + + /// Number of Joystick ports. static constexpr int kJoystickPorts = 6; /** @@ -333,9 +355,25 @@ class DriverStation final { */ static double GetBatteryVoltage(); + /** + * Copy data from the DS task for the user. If no new data exists, it will + * just be returned, otherwise the data will be copied from the DS polling + * loop. + */ static void RefreshData(); + /** + * Registers the given handle for DS data refresh notifications. + * + * @param handle The event handle. + */ static void ProvideRefreshedDataEventHandle(WPI_EventHandle handle); + + /** + * Unregisters the given handle from DS data refresh notifications. + * + * @param handle The event handle. + */ static void RemoveRefreshedDataEventHandle(WPI_EventHandle handle); /** diff --git a/wpilibc/src/main/native/include/frc/Encoder.h b/wpilibc/src/main/native/include/frc/Encoder.h index 7315e1638af..8d6c10a4556 100644 --- a/wpilibc/src/main/native/include/frc/Encoder.h +++ b/wpilibc/src/main/native/include/frc/Encoder.h @@ -42,10 +42,17 @@ class Encoder : public CounterBase, friend class DMASample; public: + /** + * Encoder indexing types. + */ enum IndexingType { + /// Reset while the signal is high. kResetWhileHigh, + /// Reset while the signal is low. kResetWhileLow, + /// Reset on falling edge of the signal. kResetOnFallingEdge, + /// Reset on rising edge of the signal. kResetOnRisingEdge }; diff --git a/wpilibc/src/main/native/include/frc/GenericHID.h b/wpilibc/src/main/native/include/frc/GenericHID.h index f6db94c3743..aac4bec7026 100644 --- a/wpilibc/src/main/native/include/frc/GenericHID.h +++ b/wpilibc/src/main/native/include/frc/GenericHID.h @@ -23,25 +23,55 @@ class EventLoop; */ class GenericHID { public: - enum RumbleType { kLeftRumble, kRightRumble, kBothRumble }; + /** + * Represents a rumble output on the Joystick. + */ + enum RumbleType { + /// Left rumble motor. + kLeftRumble, + /// Right rumble motor. + kRightRumble, + /// Both left and right rumble motors. + kBothRumble + }; + /** + * USB HID interface type. + */ enum HIDType { + /// Unknown. kUnknown = -1, + /// XInputUnknown. kXInputUnknown = 0, + /// XInputGamepad. kXInputGamepad = 1, + /// XInputWheel. kXInputWheel = 2, + /// XInputArcadeStick. kXInputArcadeStick = 3, + /// XInputFlightStick. kXInputFlightStick = 4, + /// XInputDancePad. kXInputDancePad = 5, + /// XInputGuitar. kXInputGuitar = 6, + /// XInputGuitar2. kXInputGuitar2 = 7, + /// XInputDrumKit. kXInputDrumKit = 8, + /// XInputGuitar3. kXInputGuitar3 = 11, + /// XInputArcadePad. kXInputArcadePad = 19, + /// HIDJoystick. kHIDJoystick = 20, + /// HIDGamepad. kHIDGamepad = 21, + /// HIDDriving. kHIDDriving = 22, + /// HIDFlight. kHIDFlight = 23, + /// HID1stPerson. kHID1stPerson = 24 }; diff --git a/wpilibc/src/main/native/include/frc/I2C.h b/wpilibc/src/main/native/include/frc/I2C.h index 9489fcfb3ee..07f3d4a927c 100644 --- a/wpilibc/src/main/native/include/frc/I2C.h +++ b/wpilibc/src/main/native/include/frc/I2C.h @@ -22,7 +22,15 @@ namespace frc { */ class I2C { public: - enum Port { kOnboard = 0, kMXP }; + /** + * I2C connection ports. + */ + enum Port { + /// Onboard I2C port. + kOnboard = 0, + /// MXP (roboRIO MXP) I2C port. + kMXP + }; /** * Constructor. @@ -37,7 +45,18 @@ class I2C { I2C(I2C&&) = default; I2C& operator=(I2C&&) = default; + /** + * Returns I2C port. + * + * @return I2C port. + */ Port GetPort() const; + + /** + * Returns I2C device address. + * + * @return I2C device address. + */ int GetDeviceAddress() const; /** diff --git a/wpilibc/src/main/native/include/frc/IterativeRobotBase.h b/wpilibc/src/main/native/include/frc/IterativeRobotBase.h index 79c1de2cbc2..be7687d31de 100644 --- a/wpilibc/src/main/native/include/frc/IterativeRobotBase.h +++ b/wpilibc/src/main/native/include/frc/IterativeRobotBase.h @@ -243,6 +243,9 @@ class IterativeRobotBase : public RobotBase { IterativeRobotBase(IterativeRobotBase&&) = default; IterativeRobotBase& operator=(IterativeRobotBase&&) = default; + /** + * Loop function. + */ void LoopFunc(); private: diff --git a/wpilibc/src/main/native/include/frc/Joystick.h b/wpilibc/src/main/native/include/frc/Joystick.h index a955718c3b0..d9c4144119f 100644 --- a/wpilibc/src/main/native/include/frc/Joystick.h +++ b/wpilibc/src/main/native/include/frc/Joystick.h @@ -22,14 +22,42 @@ namespace frc { */ class Joystick : public GenericHID { public: + /// Default X axis channel. static constexpr int kDefaultXChannel = 0; + /// Default Y axis channel. static constexpr int kDefaultYChannel = 1; + /// Default Z axis channel. static constexpr int kDefaultZChannel = 2; + /// Default twist axis channel. static constexpr int kDefaultTwistChannel = 2; + /// Default throttle axis channel. static constexpr int kDefaultThrottleChannel = 3; - enum AxisType { kXAxis, kYAxis, kZAxis, kTwistAxis, kThrottleAxis }; - enum ButtonType { kTriggerButton, kTopButton }; + /** + * Represents an analog axis on a joystick. + */ + enum AxisType { + /// X axis. + kXAxis, + /// Y axis. + kYAxis, + /// Z axis. + kZAxis, + /// Twist axis. + kTwistAxis, + /// Throttle axis. + kThrottleAxis + }; + + /** + * Represents a digital button on a joystick. + */ + enum ButtonType { + /// kTrigger. + kTriggerButton, + /// kTop. + kTopButton + }; /** * Construct an instance of a joystick. diff --git a/wpilibc/src/main/native/include/frc/MotorSafety.h b/wpilibc/src/main/native/include/frc/MotorSafety.h index a17cdc0cabd..efe7806a951 100644 --- a/wpilibc/src/main/native/include/frc/MotorSafety.h +++ b/wpilibc/src/main/native/include/frc/MotorSafety.h @@ -93,12 +93,15 @@ class MotorSafety { */ static void CheckMotors(); + /** + * Called to stop the motor when the timeout expires. + */ virtual void StopMotor() = 0; /** - * The return value from this method is printed out when an error occurs + * Returns a description to print when an error occurs. * - * This method must not throw! + * @return Description to print when an error occurs. */ virtual std::string GetDescription() const = 0; diff --git a/wpilibc/src/main/native/include/frc/PS4Controller.h b/wpilibc/src/main/native/include/frc/PS4Controller.h index 0a72f7fb727..881f8564f25 100644 --- a/wpilibc/src/main/native/include/frc/PS4Controller.h +++ b/wpilibc/src/main/native/include/frc/PS4Controller.h @@ -503,29 +503,55 @@ class PS4Controller : public GenericHID { */ BooleanEvent Touchpad(EventLoop* loop) const; + /** + * Represents a digital button on a PS4Controller. + */ struct Button { + /// Square button. static constexpr int kSquare = 1; + /// X button. static constexpr int kCross = 2; + /// Circle button. static constexpr int kCircle = 3; + /// Triangle button. static constexpr int kTriangle = 4; + /// Left Trigger 1 button. static constexpr int kL1 = 5; + /// Right Trigger 1 button. static constexpr int kR1 = 6; + /// Left Trigger 2 button. static constexpr int kL2 = 7; + /// Right Trigger 2 button. static constexpr int kR2 = 8; + /// Share button. static constexpr int kShare = 9; + /// Option button. static constexpr int kOptions = 10; + /// Left stick button. static constexpr int kL3 = 11; + /// Right stick button. static constexpr int kR3 = 12; + /// PlayStation button. static constexpr int kPS = 13; + /// Touchpad click button. static constexpr int kTouchpad = 14; }; + /** + * Represents an axis on a PS4Controller. + */ struct Axis { + /// Left X axis. static constexpr int kLeftX = 0; + /// Left Y axis. static constexpr int kLeftY = 1; + /// Right X axis. static constexpr int kRightX = 2; + /// Right Y axis. static constexpr int kRightY = 5; + /// Left Trigger 2. static constexpr int kL2 = 3; + /// Right Trigger 2. static constexpr int kR2 = 4; }; }; diff --git a/wpilibc/src/main/native/include/frc/PS5Controller.h b/wpilibc/src/main/native/include/frc/PS5Controller.h index 15a0a0515e8..e9d52352304 100644 --- a/wpilibc/src/main/native/include/frc/PS5Controller.h +++ b/wpilibc/src/main/native/include/frc/PS5Controller.h @@ -503,29 +503,55 @@ class PS5Controller : public GenericHID { */ BooleanEvent Touchpad(EventLoop* loop) const; + /** + * Represents a digital button on a PS5Controller. + */ struct Button { + /// Square button. static constexpr int kSquare = 1; + /// X button. static constexpr int kCross = 2; + /// Circle button. static constexpr int kCircle = 3; + /// Triangle button. static constexpr int kTriangle = 4; + /// Left trigger 1 button. static constexpr int kL1 = 5; + /// Right trigger 1 button. static constexpr int kR1 = 6; + /// Left trigger 2 button. static constexpr int kL2 = 7; + /// Right trigger 2 button. static constexpr int kR2 = 8; + /// Create button. static constexpr int kCreate = 9; + /// Options button. static constexpr int kOptions = 10; + /// Left stick button. static constexpr int kL3 = 11; + /// Right stick button. static constexpr int kR3 = 12; + /// PlayStation button. static constexpr int kPS = 13; + /// Touchpad click button. static constexpr int kTouchpad = 14; }; + /** + * Represents an axis on a PS5Controller. + */ struct Axis { + /// Left X axis. static constexpr int kLeftX = 0; + /// Left Y axis. static constexpr int kLeftY = 1; + /// Right X axis. static constexpr int kRightX = 2; + /// Right Y axis. static constexpr int kRightY = 5; + /// Left Trigger 2. static constexpr int kL2 = 3; + /// Right Trigger 2. static constexpr int kR2 = 4; }; }; diff --git a/wpilibc/src/main/native/include/frc/PWM.h b/wpilibc/src/main/native/include/frc/PWM.h index 0871c9c527d..c1f162ee816 100644 --- a/wpilibc/src/main/native/include/frc/PWM.h +++ b/wpilibc/src/main/native/include/frc/PWM.h @@ -144,6 +144,9 @@ class PWM : public wpi::Sendable, public wpi::SendableHelper { */ void SetPeriodMultiplier(PeriodMultiplier mult); + /** + * Latches PWM to zero. + */ void SetZeroLatch(); /** diff --git a/wpilibc/src/main/native/include/frc/PneumaticsBase.h b/wpilibc/src/main/native/include/frc/PneumaticsBase.h index 59f899fabbd..52ae9b0bf03 100644 --- a/wpilibc/src/main/native/include/frc/PneumaticsBase.h +++ b/wpilibc/src/main/native/include/frc/PneumaticsBase.h @@ -18,6 +18,10 @@ namespace frc { class Solenoid; class DoubleSolenoid; class Compressor; + +/** + * Base class for pneumatics devices. + */ class PneumaticsBase { public: virtual ~PneumaticsBase() = default; diff --git a/wpilibc/src/main/native/include/frc/PneumaticsModuleType.h b/wpilibc/src/main/native/include/frc/PneumaticsModuleType.h index 7f706620b51..4fb225a217e 100644 --- a/wpilibc/src/main/native/include/frc/PneumaticsModuleType.h +++ b/wpilibc/src/main/native/include/frc/PneumaticsModuleType.h @@ -5,5 +5,13 @@ #pragma once namespace frc { -enum class PneumaticsModuleType { CTREPCM, REVPH }; +/** + * Pneumatics module type. + */ +enum class PneumaticsModuleType { + /// CTRE PCM. + CTREPCM, + /// REV PH. + REVPH +}; } // namespace frc diff --git a/wpilibc/src/main/native/include/frc/PowerDistribution.h b/wpilibc/src/main/native/include/frc/PowerDistribution.h index 8f49f71b841..4a6eb4fa185 100644 --- a/wpilibc/src/main/native/include/frc/PowerDistribution.h +++ b/wpilibc/src/main/native/include/frc/PowerDistribution.h @@ -17,8 +17,18 @@ namespace frc { class PowerDistribution : public wpi::Sendable, public wpi::SendableHelper { public: + /// Default module number. static constexpr int kDefaultModule = -1; - enum class ModuleType { kCTRE = 1, kRev = 2 }; + + /** + * Power distribution module type. + */ + enum class ModuleType { + /// CTRE (Cross The Road Electronics) CTRE Power Distribution Panel (PDP). + kCTRE = 1, + /// REV Power Distribution Hub (PDH). + kRev = 2 + }; /** * Constructs a PowerDistribution object. @@ -170,6 +180,11 @@ class PowerDistribution : public wpi::Sendable, bool GetBreakerFault(int channel) const; }; + /** + * Returns the power distribution faults. + * + * @return The power distribution faults. + */ Faults GetFaults() const; struct StickyFaults { @@ -213,6 +228,11 @@ class PowerDistribution : public wpi::Sendable, bool GetBreakerFault(int channel) const; }; + /** + * Returns the power distribution sticky faults. + * + * @return The power distribution sticky faults. + */ StickyFaults GetStickyFaults() const; void InitSendable(wpi::SendableBuilder& builder) override; diff --git a/wpilibc/src/main/native/include/frc/Relay.h b/wpilibc/src/main/native/include/frc/Relay.h index 4765c64a6a4..d08a0007daf 100644 --- a/wpilibc/src/main/native/include/frc/Relay.h +++ b/wpilibc/src/main/native/include/frc/Relay.h @@ -31,8 +31,31 @@ class Relay : public MotorSafety, public wpi::Sendable, public wpi::SendableHelper { public: - enum Value { kOff, kOn, kForward, kReverse }; - enum Direction { kBothDirections, kForwardOnly, kReverseOnly }; + /** + * The state to drive a Relay to. + */ + enum Value { + /// Off. + kOff, + /// On. + kOn, + /// Forward. + kForward, + /// Reverse. + kReverse + }; + + /** + * The Direction(s) that a relay is configured to operate in. + */ + enum Direction { + /// Both directions are valid. + kBothDirections, + /// Only forward is valid. + kForwardOnly, + /// Only reverse is valid. + kReverseOnly + }; /** * Relay constructor given a channel. diff --git a/wpilibc/src/main/native/include/frc/RobotBase.h b/wpilibc/src/main/native/include/frc/RobotBase.h index 504ac810f81..6dff3696cdd 100644 --- a/wpilibc/src/main/native/include/frc/RobotBase.h +++ b/wpilibc/src/main/native/include/frc/RobotBase.h @@ -189,7 +189,9 @@ class RobotBase { bool IsTestEnabled() const; /** - * Gets the ID of the main robot thread. + * Returns the main thread ID. + * + * @return The main thread ID. */ static std::thread::id GetThreadId(); diff --git a/wpilibc/src/main/native/include/frc/RobotController.h b/wpilibc/src/main/native/include/frc/RobotController.h index 0e7a4665017..5d8ddfb4de2 100644 --- a/wpilibc/src/main/native/include/frc/RobotController.h +++ b/wpilibc/src/main/native/include/frc/RobotController.h @@ -83,6 +83,10 @@ class RobotController { /** * Get the state of the "USER" button on the roboRIO. * + * @warning the User Button is used to stop user programs from automatically + * loading if it is held for more then 5 seconds. Because of this, it's not + * recommended to be used by teams for any other purpose. + * * @return True if the button is currently pressed down */ static bool GetUserButton(); diff --git a/wpilibc/src/main/native/include/frc/RobotState.h b/wpilibc/src/main/native/include/frc/RobotState.h index 0489b9bb742..ec8700e34ac 100644 --- a/wpilibc/src/main/native/include/frc/RobotState.h +++ b/wpilibc/src/main/native/include/frc/RobotState.h @@ -6,15 +6,53 @@ namespace frc { +/** + * Robot state utility functions. + */ class RobotState { public: RobotState() = delete; + /** + * Returns true if the robot is disabled. + * + * @return True if the robot is disabled. + */ static bool IsDisabled(); + + /** + * Returns true if the robot is enabled. + * + * @return True if the robot is enabled. + */ static bool IsEnabled(); + + /** + * Returns true if the robot is E-stopped. + * + * @return True if the robot is E-stopped. + */ static bool IsEStopped(); + + /** + * Returns true if the robot is in teleop mode. + * + * @return True if the robot is in teleop mode. + */ static bool IsTeleop(); + + /** + * Returns true if the robot is in autonomous mode. + * + * @return True if the robot is in autonomous mode. + */ static bool IsAutonomous(); + + /** + * Returns true if the robot is in test mode. + * + * @return True if the robot is in test mode. + */ static bool IsTest(); }; diff --git a/wpilibc/src/main/native/include/frc/RuntimeType.h b/wpilibc/src/main/native/include/frc/RuntimeType.h index c3a8a0bb71a..2be444a60f6 100644 --- a/wpilibc/src/main/native/include/frc/RuntimeType.h +++ b/wpilibc/src/main/native/include/frc/RuntimeType.h @@ -5,5 +5,15 @@ #pragma once namespace frc { -enum RuntimeType { kRoboRIO, kRoboRIO2, kSimulation }; +/** + * Runtime type. + */ +enum RuntimeType { + /// roboRIO 1.0. + kRoboRIO, + /// roboRIO 2.0. + kRoboRIO2, + /// Simulation runtime. + kSimulation +}; } // namespace frc diff --git a/wpilibc/src/main/native/include/frc/SPI.h b/wpilibc/src/main/native/include/frc/SPI.h index 1158cbdea2a..5b170bac230 100644 --- a/wpilibc/src/main/native/include/frc/SPI.h +++ b/wpilibc/src/main/native/include/frc/SPI.h @@ -25,14 +25,34 @@ class DigitalSource; */ class SPI { public: - enum Port { kOnboardCS0 = 0, kOnboardCS1, kOnboardCS2, kOnboardCS3, kMXP }; + /** + * SPI port. + */ + enum Port { + /// Onboard SPI bus port CS0. + kOnboardCS0 = 0, + /// Onboard SPI bus port CS1. + kOnboardCS1, + /// Onboard SPI bus port CS2. + kOnboardCS2, + /// Onboard SPI bus port CS3. + kOnboardCS3, + /// MXP (roboRIO MXP) SPI bus port. + kMXP + }; + + /** + * SPI mode. + */ enum Mode { - kMode0 = HAL_SPI_kMode0, /*!< Clock idle low, data sampled on rising edge */ - kMode1 = - HAL_SPI_kMode1, /*!< Clock idle low, data sampled on falling edge */ - kMode2 = - HAL_SPI_kMode2, /*!< Clock idle high, data sampled on falling edge */ - kMode3 = HAL_SPI_kMode3 /*!< Clock idle high, data sampled on rising edge */ + /// Clock idle low, data sampled on rising edge. + kMode0 = HAL_SPI_kMode0, + /// Clock idle low, data sampled on falling edge. + kMode1 = HAL_SPI_kMode1, + /// Clock idle high, data sampled on falling edge. + kMode2 = HAL_SPI_kMode2, + /// Clock idle high, data sampled on rising edge. + kMode3 = HAL_SPI_kMode3 }; /** @@ -47,6 +67,11 @@ class SPI { SPI(SPI&&) = default; SPI& operator=(SPI&&) = default; + /** + * Returns the SPI port. + * + * @return The SPI port. + */ Port GetPort() const; /** diff --git a/wpilibc/src/main/native/include/frc/SerialPort.h b/wpilibc/src/main/native/include/frc/SerialPort.h index dcc0a2e068a..b7784da794b 100644 --- a/wpilibc/src/main/native/include/frc/SerialPort.h +++ b/wpilibc/src/main/native/include/frc/SerialPort.h @@ -26,30 +26,73 @@ namespace frc { */ class SerialPort { public: + /** + * Serial port. + */ + enum Port { + /// Onboard serial port on the roboRIO. + kOnboard = 0, + /// MXP (roboRIO MXP) serial port. + kMXP = 1, + /// USB serial port (same as KUSB1). + kUSB = 2, + /// USB serial port 1. + kUSB1 = 2, + /// USB serial port 2. + kUSB2 = 3 + }; + + /** + * Represents the parity to use for serial communications. + */ enum Parity { + /// No parity. kParity_None = 0, + /// Odd parity. kParity_Odd = 1, + /// Even parity. kParity_Even = 2, + /// Parity bit always on. kParity_Mark = 3, + /// Parity bit always off. kParity_Space = 4 }; + /** + * Represents the number of stop bits to use for Serial Communication. + */ enum StopBits { + /// One stop bit. kStopBits_One = 10, + /// One and a half stop bits. kStopBits_OnePointFive = 15, + /// Two stop bits. kStopBits_Two = 20 }; + /** + * Represents what type of flow control to use for serial communication. + */ enum FlowControl { + /// No flow control. kFlowControl_None = 0, + /// XON/XOFF flow control. kFlowControl_XonXoff = 1, + /// RTS/CTS flow control. kFlowControl_RtsCts = 2, + /// DTS/DSR flow control. kFlowControl_DtrDsr = 4 }; - enum WriteBufferMode { kFlushOnAccess = 1, kFlushWhenFull = 2 }; - - enum Port { kOnboard = 0, kMXP = 1, kUSB = 2, kUSB1 = 2, kUSB2 = 3 }; + /** + * Represents which type of buffer mode to use when writing to a serial port. + */ + enum WriteBufferMode { + /// Flush the buffer on each access. + kFlushOnAccess = 1, + /// Flush the buffer when it is full. + kFlushWhenFull = 2 + }; /** * Create an instance of a Serial Port class. diff --git a/wpilibc/src/main/native/include/frc/Servo.h b/wpilibc/src/main/native/include/frc/Servo.h index 9383641a2cd..76b39a0d90e 100644 --- a/wpilibc/src/main/native/include/frc/Servo.h +++ b/wpilibc/src/main/native/include/frc/Servo.h @@ -19,6 +19,11 @@ namespace frc { class Servo : public PWM { public: /** + * Constructor. + * + * By default, 2.4 ms is used as the max PWM value and 0.6 ms is used as the + * min PWM value. + * * @param channel The PWM channel to which the servo is attached. 0-9 are * on-board, 10-19 are on the MXP port */ diff --git a/wpilibc/src/main/native/include/frc/StadiaController.h b/wpilibc/src/main/native/include/frc/StadiaController.h index 9d978e9e090..cc9dbae981c 100644 --- a/wpilibc/src/main/native/include/frc/StadiaController.h +++ b/wpilibc/src/main/native/include/frc/StadiaController.h @@ -514,28 +514,53 @@ class StadiaController : public GenericHID { */ BooleanEvent RightTrigger(EventLoop* loop) const; + /** + * Represents a digital button on a StadiaController. + */ struct Button { + /// A button. static constexpr int kA = 1; + /// B button. static constexpr int kB = 2; + /// X button. static constexpr int kX = 3; + /// Y button. static constexpr int kY = 4; + /// Left bumper button. static constexpr int kLeftBumper = 5; + /// Right bumper button. static constexpr int kRightBumper = 6; + /// Left stick button. static constexpr int kLeftStick = 7; + /// Right stick button. static constexpr int kRightStick = 8; + /// Ellipses button. static constexpr int kEllipses = 9; + /// Hamburger button. static constexpr int kHamburger = 10; + /// Stadia button. static constexpr int kStadia = 11; + /// Right trigger button. static constexpr int kRightTrigger = 12; + /// Left trigger button. static constexpr int kLeftTrigger = 13; + /// Google button. static constexpr int kGoogle = 14; + /// Frame button. static constexpr int kFrame = 15; }; + /** + * Represents an axis on a StadiaController. + */ struct Axis { + /// Left X axis. static constexpr int kLeftX = 0; + /// Right X axis. static constexpr int kRightX = 4; + /// Left Y axis. static constexpr int kLeftY = 1; + /// Right Y axis. static constexpr int kRightY = 5; }; }; diff --git a/wpilibc/src/main/native/include/frc/SynchronousInterrupt.h b/wpilibc/src/main/native/include/frc/SynchronousInterrupt.h index fbe0fca680e..3e396e85779 100644 --- a/wpilibc/src/main/native/include/frc/SynchronousInterrupt.h +++ b/wpilibc/src/main/native/include/frc/SynchronousInterrupt.h @@ -25,9 +25,13 @@ class SynchronousInterrupt { * Event trigger combinations for a synchronous interrupt. */ enum WaitResult { + /// Timeout event. kTimeout = 0x0, + /// Rising edge event. kRisingEdge = 0x1, + /// Falling edge event. kFallingEdge = 0x100, + /// Both rising and falling edge events. kBoth = 0x101, }; diff --git a/wpilibc/src/main/native/include/frc/TimedRobot.h b/wpilibc/src/main/native/include/frc/TimedRobot.h index f32e7483cf9..dcc510af2b8 100644 --- a/wpilibc/src/main/native/include/frc/TimedRobot.h +++ b/wpilibc/src/main/native/include/frc/TimedRobot.h @@ -29,6 +29,7 @@ namespace frc { */ class TimedRobot : public IterativeRobotBase { public: + /// Default loop period. static constexpr auto kDefaultPeriod = 20_ms; /** diff --git a/wpilibc/src/main/native/include/frc/Ultrasonic.h b/wpilibc/src/main/native/include/frc/Ultrasonic.h index 137a5b75aeb..f06392c9310 100644 --- a/wpilibc/src/main/native/include/frc/Ultrasonic.h +++ b/wpilibc/src/main/native/include/frc/Ultrasonic.h @@ -90,6 +90,11 @@ class Ultrasonic : public wpi::Sendable, Ultrasonic(Ultrasonic&&) = default; Ultrasonic& operator=(Ultrasonic&&) = default; + /** + * Returns the echo channel. + * + * @return The echo channel. + */ int GetEchoChannel() const; /** diff --git a/wpilibc/src/main/native/include/frc/XboxController.h b/wpilibc/src/main/native/include/frc/XboxController.h index 775e84d03b5..4b720c1fc72 100644 --- a/wpilibc/src/main/native/include/frc/XboxController.h +++ b/wpilibc/src/main/native/include/frc/XboxController.h @@ -426,25 +426,43 @@ class XboxController : public GenericHID { */ BooleanEvent RightTrigger(EventLoop* loop) const; + /** Represents a digital button on an XboxController. */ struct Button { + /// Left bumper. static constexpr int kLeftBumper = 5; + /// Right bumper. static constexpr int kRightBumper = 6; + /// Left stick. static constexpr int kLeftStick = 9; + /// Right stick. static constexpr int kRightStick = 10; + /// A. static constexpr int kA = 1; + /// B. static constexpr int kB = 2; + /// X. static constexpr int kX = 3; + /// Y. static constexpr int kY = 4; + /// Back. static constexpr int kBack = 7; + /// Start. static constexpr int kStart = 8; }; + /** Represents an axis on an XboxController. */ struct Axis { + /// Left X. static constexpr int kLeftX = 0; + /// Right X. static constexpr int kRightX = 4; + /// Left Y. static constexpr int kLeftY = 1; + /// Right Y. static constexpr int kRightY = 5; + /// Left trigger. static constexpr int kLeftTrigger = 2; + /// Right trigger. static constexpr int kRightTrigger = 3; }; }; diff --git a/wpilibc/src/main/native/include/frc/counter/EdgeConfiguration.h b/wpilibc/src/main/native/include/frc/counter/EdgeConfiguration.h index 508117d79ff..4f5a039de2a 100644 --- a/wpilibc/src/main/native/include/frc/counter/EdgeConfiguration.h +++ b/wpilibc/src/main/native/include/frc/counter/EdgeConfiguration.h @@ -5,10 +5,17 @@ #pragma once namespace frc { +/** + * Edge configuration. + */ enum class EdgeConfiguration { + /// No edge configuration (neither rising nor falling). kNone = 0, + /// Rising edge configuration. kRisingEdge = 0x1, + /// Falling edge configuration. kFallingEdge = 0x2, + /// Both rising and falling edges configuration. kBoth = 0x3 }; } // namespace frc diff --git a/wpilibc/src/main/native/include/frc/drive/DifferentialDrive.h b/wpilibc/src/main/native/include/frc/drive/DifferentialDrive.h index 2e701a38179..2baa49effa5 100644 --- a/wpilibc/src/main/native/include/frc/drive/DifferentialDrive.h +++ b/wpilibc/src/main/native/include/frc/drive/DifferentialDrive.h @@ -4,8 +4,10 @@ #pragma once +#include #include +#include #include #include @@ -20,43 +22,9 @@ class MotorController; * the Kit of Parts drive base, "tank drive", or West Coast Drive. * * These drive bases typically have drop-center / skid-steer with two or more - * wheels per side (e.g., 6WD or 8WD). This class takes a MotorController per - * side. For four and six motor drivetrains, construct and pass in - * MotorControllerGroup instances as follows. - * - * Four motor drivetrain: - * @code{.cpp} - * class Robot { - * public: - * frc::PWMVictorSPX m_frontLeft{1}; - * frc::PWMVictorSPX m_rearLeft{2}; - * frc::MotorControllerGroup m_left{m_frontLeft, m_rearLeft}; - * - * frc::PWMVictorSPX m_frontRight{3}; - * frc::PWMVictorSPX m_rearRight{4}; - * frc::MotorControllerGroup m_right{m_frontRight, m_rearRight}; - * - * frc::DifferentialDrive m_drive{m_left, m_right}; - * }; - * @endcode - * - * Six motor drivetrain: - * @code{.cpp} - * class Robot { - * public: - * frc::PWMVictorSPX m_frontLeft{1}; - * frc::PWMVictorSPX m_midLeft{2}; - * frc::PWMVictorSPX m_rearLeft{3}; - * frc::MotorControllerGroup m_left{m_frontLeft, m_midLeft, m_rearLeft}; - * - * frc::PWMVictorSPX m_frontRight{4}; - * frc::PWMVictorSPX m_midRight{5}; - * frc::PWMVictorSPX m_rearRight{6}; - * frc::MotorControllerGroup m_right{m_frontRight, m_midRight, m_rearRight}; - * - * frc::DifferentialDrive m_drive{m_left, m_right}; - * }; - * @endcode + * wheels per side (e.g., 6WD or 8WD). This class takes a setter per side. For + * four and six motor drivetrains, use CAN motor controller followers or + * PWMMotorController::AddFollower(). * * A differential drive robot has left and right wheels separated by an * arbitrary width. @@ -97,18 +65,41 @@ class DifferentialDrive : public RobotDriveBase, * Uses normalized voltage [-1.0..1.0]. */ struct WheelSpeeds { + /// Left wheel speed. double left = 0.0; + /// Right wheel speed. double right = 0.0; }; + WPI_IGNORE_DEPRECATED + /** * Construct a DifferentialDrive. * - * To pass multiple motors per side, use a MotorControllerGroup. If a motor - * needs to be inverted, do so before passing it in. + * To pass multiple motors per side, use CAN motor controller followers or + * PWMSpeedController::AddFollower(). If a motor needs to be inverted, do so + * before passing it in. + * + * @param leftMotor Left motor. + * @param rightMotor Right motor. */ DifferentialDrive(MotorController& leftMotor, MotorController& rightMotor); + WPI_UNIGNORE_DEPRECATED + + /** + * Construct a DifferentialDrive. + * + * To pass multiple motors per side, use CAN motor controller followers or + * PWMSpeedController::AddFollower(). If a motor needs to be inverted, do so + * before passing it in. + * + * @param leftMotor Left motor setter. + * @param rightMotor Right motor setter. + */ + DifferentialDrive(std::function leftMotor, + std::function rightMotor); + ~DifferentialDrive() override = default; DifferentialDrive(DifferentialDrive&&) = default; @@ -210,8 +201,12 @@ class DifferentialDrive : public RobotDriveBase, void InitSendable(wpi::SendableBuilder& builder) override; private: - MotorController* m_leftMotor; - MotorController* m_rightMotor; + std::function m_leftMotor; + std::function m_rightMotor; + + // Used for Sendable property getters + double m_leftOutput = 0.0; + double m_rightOutput = 0.0; }; } // namespace frc diff --git a/wpilibc/src/main/native/include/frc/drive/MecanumDrive.h b/wpilibc/src/main/native/include/frc/drive/MecanumDrive.h index f4c28f423d8..6d8ebac444f 100644 --- a/wpilibc/src/main/native/include/frc/drive/MecanumDrive.h +++ b/wpilibc/src/main/native/include/frc/drive/MecanumDrive.h @@ -4,10 +4,12 @@ #pragma once +#include #include #include #include +#include #include #include @@ -63,21 +65,49 @@ class MecanumDrive : public RobotDriveBase, * Uses normalized voltage [-1.0..1.0]. */ struct WheelSpeeds { + /// Front-left wheel speed. double frontLeft = 0.0; + /// Front-right wheel speed. double frontRight = 0.0; + /// Rear-left wheel speed. double rearLeft = 0.0; + /// Rear-right wheel speed. double rearRight = 0.0; }; + WPI_IGNORE_DEPRECATED + /** * Construct a MecanumDrive. * * If a motor needs to be inverted, do so before passing it in. + * + * @param frontLeftMotor Front-left motor. + * @param rearLeftMotor Rear-left motor. + * @param frontRightMotor Front-right motor. + * @param rearRightMotor Rear-right motor. */ MecanumDrive(MotorController& frontLeftMotor, MotorController& rearLeftMotor, MotorController& frontRightMotor, MotorController& rearRightMotor); + WPI_UNIGNORE_DEPRECATED + + /** + * Construct a MecanumDrive. + * + * If a motor needs to be inverted, do so before passing it in. + * + * @param frontLeftMotor Front-left motor setter. + * @param rearLeftMotor Rear-left motor setter. + * @param frontRightMotor Front-right motor setter. + * @param rearRightMotor Rear-right motor setter. + */ + MecanumDrive(std::function frontLeftMotor, + std::function rearLeftMotor, + std::function frontRightMotor, + std::function rearRightMotor); + ~MecanumDrive() override = default; MecanumDrive(MecanumDrive&&) = default; @@ -141,10 +171,16 @@ class MecanumDrive : public RobotDriveBase, void InitSendable(wpi::SendableBuilder& builder) override; private: - MotorController* m_frontLeftMotor; - MotorController* m_rearLeftMotor; - MotorController* m_frontRightMotor; - MotorController* m_rearRightMotor; + std::function m_frontLeftMotor; + std::function m_rearLeftMotor; + std::function m_frontRightMotor; + std::function m_rearRightMotor; + + // Used for Sendable property getters + double m_frontLeftOutput = 0.0; + double m_rearLeftOutput = 0.0; + double m_frontRightOutput = 0.0; + double m_rearRightOutput = 0.0; bool reported = false; }; diff --git a/wpilibc/src/main/native/include/frc/drive/RobotDriveBase.h b/wpilibc/src/main/native/include/frc/drive/RobotDriveBase.h index b3fb56be4ba..2a746434e4e 100644 --- a/wpilibc/src/main/native/include/frc/drive/RobotDriveBase.h +++ b/wpilibc/src/main/native/include/frc/drive/RobotDriveBase.h @@ -23,12 +23,19 @@ class RobotDriveBase : public MotorSafety { * The location of a motor on the robot for the purpose of driving. */ enum MotorType { + /// Front-left motor. kFrontLeft = 0, + /// Front-right motor. kFrontRight = 1, + /// Rear-left motor. kRearLeft = 2, + /// Rear-right motor. kRearRight = 3, + /// Left motor. kLeft = 0, + /// Right motor. kRight = 1, + /// Back motor. kBack = 2 }; @@ -70,14 +77,23 @@ class RobotDriveBase : public MotorSafety { std::string GetDescription() const override = 0; protected: + /// Default input deadband. + static constexpr double kDefaultDeadband = 0.02; + + /// Default maximum output. + static constexpr double kDefaultMaxOutput = 1.0; + /** * Renormalize all wheel speeds if the magnitude of any wheel is greater than * 1.0. */ static void Desaturate(std::span wheelSpeeds); - double m_deadband = 0.02; - double m_maxOutput = 1.0; + /// Input deadband. + double m_deadband = kDefaultDeadband; + + /// Maximum output. + double m_maxOutput = kDefaultMaxOutput; }; } // namespace frc diff --git a/wpilibc/src/main/native/include/frc/event/EventLoop.h b/wpilibc/src/main/native/include/frc/event/EventLoop.h index 224dd3b545e..dca61220a5b 100644 --- a/wpilibc/src/main/native/include/frc/event/EventLoop.h +++ b/wpilibc/src/main/native/include/frc/event/EventLoop.h @@ -38,5 +38,6 @@ class EventLoop { private: std::vector> m_bindings; + bool m_running{false}; }; } // namespace frc diff --git a/wpilibc/src/main/native/include/frc/interfaces/Accelerometer.h b/wpilibc/src/main/native/include/frc/interfaces/Accelerometer.h index 77f1d5bc3bd..ceafeba581c 100644 --- a/wpilibc/src/main/native/include/frc/interfaces/Accelerometer.h +++ b/wpilibc/src/main/native/include/frc/interfaces/Accelerometer.h @@ -20,7 +20,27 @@ class [[deprecated( Accelerometer(Accelerometer&&) = default; Accelerometer& operator=(Accelerometer&&) = default; - enum Range { kRange_2G = 0, kRange_4G = 1, kRange_8G = 2, kRange_16G = 3 }; + /** + * Accelerometer range. + */ + enum Range { + /** + * 2 Gs max. + */ + kRange_2G = 0, + /** + * 4 Gs max. + */ + kRange_4G = 1, + /** + * 8 Gs max. + */ + kRange_8G = 2, + /** + * 16 Gs max. + */ + kRange_16G = 3 + }; /** * Common interface for setting the measuring range of an accelerometer. diff --git a/wpilibc/src/main/native/include/frc/internal/DriverStationModeThread.h b/wpilibc/src/main/native/include/frc/internal/DriverStationModeThread.h index f4fd11ca822..e646e15b665 100644 --- a/wpilibc/src/main/native/include/frc/internal/DriverStationModeThread.h +++ b/wpilibc/src/main/native/include/frc/internal/DriverStationModeThread.h @@ -8,9 +8,16 @@ #include namespace frc::internal { +/** + * For internal use only. + */ class DriverStationModeThread { public: + /** + * For internal use only. + */ DriverStationModeThread(); + ~DriverStationModeThread(); DriverStationModeThread(const DriverStationModeThread& other) = delete; @@ -19,9 +26,39 @@ class DriverStationModeThread { delete; DriverStationModeThread& operator=(DriverStationModeThread&& other) = delete; - void InAutonomous(bool entering); + /** + * Only to be used to tell the Driver Station what code you claim to be + * executing for diagnostic purposes only. + * + * @param entering If true, starting disabled code; if false, leaving disabled + * code + */ void InDisabled(bool entering); + + /** + * Only to be used to tell the Driver Station what code you claim to be + * executing for diagnostic purposes only. + * + * @param entering If true, starting autonomous code; if false, leaving + * autonomous code + */ + void InAutonomous(bool entering); + + /** + * Only to be used to tell the Driver Station what code you claim to be + * executing for diagnostic purposes only. + * + * @param entering If true, starting teleop code; if false, leaving teleop + * code + */ void InTeleop(bool entering); + + /** + * Only to be used to tell the Driver Station what code you claim to be + * executing for diagnostic purposes only. + * + * @param entering If true, starting test code; if false, leaving test code + */ void InTest(bool entering); private: diff --git a/wpilibc/src/main/native/include/frc/livewindow/LiveWindow.h b/wpilibc/src/main/native/include/frc/livewindow/LiveWindow.h index 3714146c433..2f0a34957ab 100644 --- a/wpilibc/src/main/native/include/frc/livewindow/LiveWindow.h +++ b/wpilibc/src/main/native/include/frc/livewindow/LiveWindow.h @@ -19,14 +19,14 @@ namespace frc { class LiveWindow final { public: /** - * Set function to be called when LiveWindow is enabled. + * Sets function to be called when LiveWindow is enabled. * * @param func function (or nullptr for none) */ static void SetEnabledCallback(std::function func); /** - * Set function to be called when LiveWindow is disabled. + * Sets function to be called when LiveWindow is disabled. * * @param func function (or nullptr for none) */ @@ -56,6 +56,11 @@ class LiveWindow final { */ static void EnableAllTelemetry(); + /** + * Returns true if LiveWindow is enabled. + * + * @return True if LiveWindow is enabled. + */ static bool IsEnabled(); /** diff --git a/wpilibc/src/main/native/include/frc/motorcontrol/MotorControllerGroup.h b/wpilibc/src/main/native/include/frc/motorcontrol/MotorControllerGroup.h index 99a9e4e164c..6500fb3e2f0 100644 --- a/wpilibc/src/main/native/include/frc/motorcontrol/MotorControllerGroup.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/MotorControllerGroup.h @@ -7,23 +7,42 @@ #include #include +#include #include #include #include "frc/motorcontrol/MotorController.h" +WPI_IGNORE_DEPRECATED + namespace frc { /** * Allows multiple MotorController objects to be linked together. */ -class MotorControllerGroup : public wpi::Sendable, - public MotorController, - public wpi::SendableHelper { +class [[deprecated( + "Use PWMMotorController::AddFollower() or if using CAN motor controllers," + "use their method of following.")]] MotorControllerGroup + : public wpi::Sendable, + public MotorController, + public wpi::SendableHelper { public: + /** + * Create a new MotorControllerGroup with the provided MotorControllers. + * + * @tparam MotorControllers The MotorController types. + * @param motorController The first MotorController to add + * @param motorControllers The MotorControllers to add + */ template explicit MotorControllerGroup(MotorController& motorController, MotorControllers&... motorControllers); + + /** + * Create a new MotorControllerGroup with the provided MotorControllers. + * + * @param motorControllers The MotorControllers to add. + */ explicit MotorControllerGroup( std::vector>&& motorControllers); @@ -50,3 +69,5 @@ class MotorControllerGroup : public wpi::Sendable, } // namespace frc #include "frc/motorcontrol/MotorControllerGroup.inc" + +WPI_UNIGNORE_DEPRECATED diff --git a/wpilibc/src/main/native/include/frc/motorcontrol/NidecBrushless.h b/wpilibc/src/main/native/include/frc/motorcontrol/NidecBrushless.h index cc95d7115d3..d50c836170f 100644 --- a/wpilibc/src/main/native/include/frc/motorcontrol/NidecBrushless.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/NidecBrushless.h @@ -6,6 +6,7 @@ #include +#include #include #include @@ -16,6 +17,8 @@ namespace frc { +WPI_IGNORE_DEPRECATED + /** * Nidec Brushless Motor. */ @@ -95,4 +98,6 @@ class NidecBrushless : public MotorController, double m_speed = 0.0; }; +WPI_UNIGNORE_DEPRECATED + } // namespace frc diff --git a/wpilibc/src/main/native/include/frc/motorcontrol/PWMMotorController.h b/wpilibc/src/main/native/include/frc/motorcontrol/PWMMotorController.h index bca5d7f64e7..54a16bbd161 100644 --- a/wpilibc/src/main/native/include/frc/motorcontrol/PWMMotorController.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/PWMMotorController.h @@ -4,9 +4,16 @@ #pragma once +#include +#include #include #include +#include +#include +#include +#include +#include #include #include @@ -17,6 +24,8 @@ namespace frc { class DMA; +WPI_IGNORE_DEPRECATED + /** * Common base class for all PWM Motor Controllers. */ @@ -40,6 +49,20 @@ class PWMMotorController : public MotorController, */ void Set(double value) override; + /** + * Sets the voltage output of the PWMMotorController. Compensates for + * the current bus voltage to ensure that the desired voltage is output even + * if the battery voltage is below 12V - highly useful when the voltage + * outputs are "meaningful" (e.g. they come from a feedforward calculation). + * + *

NOTE: This function *must* be called regularly in order for voltage + * compensation to work properly - unlike the ordinary set function, it is not + * "set it and forget it." + * + * @param output The voltage to output. + */ + void SetVoltage(units::volt_t output) override; + /** * Get the recently set value of the PWM. This value is affected by the * inversion property. If you want the value that is sent directly to the @@ -71,6 +94,24 @@ class PWMMotorController : public MotorController, */ void EnableDeadbandElimination(bool eliminateDeadband); + /** + * Make the given PWM motor controller follow the output of this one. + * + * @param follower The motor controller follower. + */ + void AddFollower(PWMMotorController& follower); + + /** + * Make the given PWM motor controller follow the output of this one. + * + * @param follower The motor controller follower. + */ + template T> + void AddFollower(T&& follower) { + m_owningFollowers.emplace_back( + std::make_unique>(std::forward(follower))); + } + protected: /** * Constructor for a PWM Motor %Controller connected via PWM. @@ -83,12 +124,17 @@ class PWMMotorController : public MotorController, void InitSendable(wpi::SendableBuilder& builder) override; + /// PWM instances for motor controller. PWM m_pwm; private: bool m_isInverted = false; + std::vector m_nonowningFollowers; + std::vector> m_owningFollowers; PWM* GetPwm() { return &m_pwm; } }; +WPI_UNIGNORE_DEPRECATED + } // namespace frc diff --git a/wpilibc/src/main/native/include/frc/shuffleboard/ShuffleboardComponent.h b/wpilibc/src/main/native/include/frc/shuffleboard/ShuffleboardComponent.h index fc159482317..8cb867388bc 100644 --- a/wpilibc/src/main/native/include/frc/shuffleboard/ShuffleboardComponent.h +++ b/wpilibc/src/main/native/include/frc/shuffleboard/ShuffleboardComponent.h @@ -26,6 +26,13 @@ class ShuffleboardContainer; template class ShuffleboardComponent : public ShuffleboardComponentBase { public: + /** + * Constructs a ShuffleboardComponent. + * + * @param parent The parent container. + * @param title The component title. + * @param type The component type. + */ ShuffleboardComponent(ShuffleboardContainer& parent, std::string_view title, std::string_view type = ""); diff --git a/wpilibc/src/main/native/include/frc/shuffleboard/ShuffleboardEventImportance.h b/wpilibc/src/main/native/include/frc/shuffleboard/ShuffleboardEventImportance.h index 1fa9c91f3d6..ea251ec9158 100644 --- a/wpilibc/src/main/native/include/frc/shuffleboard/ShuffleboardEventImportance.h +++ b/wpilibc/src/main/native/include/frc/shuffleboard/ShuffleboardEventImportance.h @@ -14,6 +14,11 @@ namespace frc { enum ShuffleboardEventImportance { kTrivial, kLow, kNormal, kHigh, kCritical }; +/** + * Returns name of the given enum. + * + * @return Name of the given enum. + */ inline std::string_view ShuffleboardEventImportanceName( ShuffleboardEventImportance importance) { switch (importance) { diff --git a/wpilibc/src/main/native/include/frc/simulation/ADXL345Sim.h b/wpilibc/src/main/native/include/frc/simulation/ADXL345Sim.h index af96b39e748..3cf6162a0a3 100644 --- a/wpilibc/src/main/native/include/frc/simulation/ADXL345Sim.h +++ b/wpilibc/src/main/native/include/frc/simulation/ADXL345Sim.h @@ -14,7 +14,7 @@ class ADXL345_I2C; namespace sim { /** - * Class to control a simulated ADXRS450 gyroscope. + * Class to control a simulated ADXL345. */ class ADXL345Sim { public: diff --git a/wpilibc/src/main/native/include/frc/simulation/ADXL362Sim.h b/wpilibc/src/main/native/include/frc/simulation/ADXL362Sim.h index 269732b3bfc..cc63af27843 100644 --- a/wpilibc/src/main/native/include/frc/simulation/ADXL362Sim.h +++ b/wpilibc/src/main/native/include/frc/simulation/ADXL362Sim.h @@ -13,7 +13,7 @@ class ADXL362; namespace sim { /** - * Class to control a simulated ADXRS450 gyroscope. + * Class to control a simulated ADXL362. */ class ADXL362Sim { public: diff --git a/wpilibc/src/main/native/include/frc/simulation/DifferentialDrivetrainSim.h b/wpilibc/src/main/native/include/frc/simulation/DifferentialDrivetrainSim.h index 78310d78c74..644e348afd8 100644 --- a/wpilibc/src/main/native/include/frc/simulation/DifferentialDrivetrainSim.h +++ b/wpilibc/src/main/native/include/frc/simulation/DifferentialDrivetrainSim.h @@ -187,6 +187,13 @@ class DifferentialDrivetrainSim { */ void SetPose(const frc::Pose2d& pose); + /** + * The differential drive dynamics function. + * + * @param x The state. + * @param u The input. + * @return The state derivative with respect to time. + */ Vectord<7> Dynamics(const Vectord<7>& x, const Eigen::Vector2d& u); class State { @@ -210,32 +217,54 @@ class DifferentialDrivetrainSim { */ class KitbotGearing { public: + /// Gear ratio of 12.75:1. static constexpr double k12p75 = 12.75; + /// Gear ratio of 10.71:1. static constexpr double k10p71 = 10.71; + /// Gear ratio of 8.45:1. static constexpr double k8p45 = 8.45; + /// Gear ratio of 7.31:1. static constexpr double k7p31 = 7.31; + /// Gear ratio of 5.95:1. static constexpr double k5p95 = 5.95; }; + /** + * Represents common motor layouts of the kit drivetrain. + */ class KitbotMotor { public: + /// One CIM motor per drive side. static constexpr frc::DCMotor SingleCIMPerSide = frc::DCMotor::CIM(1); + /// Two CIM motors per drive side. static constexpr frc::DCMotor DualCIMPerSide = frc::DCMotor::CIM(2); + /// One Mini CIM motor per drive side. static constexpr frc::DCMotor SingleMiniCIMPerSide = frc::DCMotor::MiniCIM(1); + /// Two Mini CIM motors per drive side. static constexpr frc::DCMotor DualMiniCIMPerSide = frc::DCMotor::MiniCIM(2); + /// One Falcon 500 motor per drive side. static constexpr frc::DCMotor SingleFalcon500PerSide = frc::DCMotor::Falcon500(1); + /// Two Falcon 500 motors per drive side. static constexpr frc::DCMotor DualFalcon500PerSide = frc::DCMotor::Falcon500(2); + /// One NEO motor per drive side. static constexpr frc::DCMotor SingleNEOPerSide = frc::DCMotor::NEO(1); + /// Two NEO motors per drive side. static constexpr frc::DCMotor DualNEOPerSide = frc::DCMotor::NEO(2); }; + /** + * Represents common wheel sizes of the kit drivetrain. + */ class KitbotWheelSize { public: + /// Six inch diameter wheels. static constexpr units::meter_t kSixInch = 6_in; + /// Eight inch diameter wheels. static constexpr units::meter_t kEightInch = 8_in; + /// Ten inch diameter wheels. static constexpr units::meter_t kTenInch = 10_in; }; diff --git a/wpilibc/src/main/native/include/frc/simulation/GenericHIDSim.h b/wpilibc/src/main/native/include/frc/simulation/GenericHIDSim.h index 399e0c16d16..ba56c4d106a 100644 --- a/wpilibc/src/main/native/include/frc/simulation/GenericHIDSim.h +++ b/wpilibc/src/main/native/include/frc/simulation/GenericHIDSim.h @@ -136,6 +136,7 @@ class GenericHIDSim { double GetRumble(GenericHID::RumbleType type); protected: + /// GenericHID port. int m_port; }; diff --git a/wpilibc/src/main/native/include/frc/simulation/LinearSystemSim.h b/wpilibc/src/main/native/include/frc/simulation/LinearSystemSim.h index cfc87b29499..a4fec0028d7 100644 --- a/wpilibc/src/main/native/include/frc/simulation/LinearSystemSim.h +++ b/wpilibc/src/main/native/include/frc/simulation/LinearSystemSim.h @@ -23,9 +23,9 @@ namespace frc::sim { * voltage). Call the Update() method to update the simulation. Set simulated * sensor readings with the simulated positions in the GetOutput() method. * - * @tparam States The number of states of the system. - * @tparam Inputs The number of inputs to the system. - * @tparam Outputs The number of outputs of the system. + * @tparam States Number of states of the system. + * @tparam Inputs Number of inputs to the system. + * @tparam Outputs Number of outputs of the system. */ template class LinearSystemSim { @@ -140,11 +140,20 @@ class LinearSystemSim { u, frc::RobotController::GetInputVoltage()); } + /// The plant that represents the linear system. LinearSystem m_plant; + /// State vector. Vectord m_x; - Vectord m_y; + + /// Input vector. Vectord m_u; + + /// Output vector. + Vectord m_y; + + /// The standard deviations of measurements, used for adding noise to the + /// measurements. std::array m_measurementStdDevs; }; } // namespace frc::sim diff --git a/wpilibc/src/main/native/include/frc/simulation/PneumaticsBaseSim.h b/wpilibc/src/main/native/include/frc/simulation/PneumaticsBaseSim.h index 510349f7695..f3000f4a3ed 100644 --- a/wpilibc/src/main/native/include/frc/simulation/PneumaticsBaseSim.h +++ b/wpilibc/src/main/native/include/frc/simulation/PneumaticsBaseSim.h @@ -177,9 +177,22 @@ class PneumaticsBaseSim { virtual void ResetData() = 0; protected: + /// PneumaticsBase index. const int m_index; - explicit PneumaticsBaseSim(const PneumaticsBase& module); + + /** + * Constructs a PneumaticsBaseSim with the given index. + * + * @param index The index. + */ explicit PneumaticsBaseSim(const int index); + + /** + * Constructs a PneumaticsBaseSim for the given module. + * + * @param module The module. + */ + explicit PneumaticsBaseSim(const PneumaticsBase& module); }; } // namespace frc::sim diff --git a/wpilibc/src/main/native/include/frc/simulation/UltrasonicSim.h b/wpilibc/src/main/native/include/frc/simulation/UltrasonicSim.h index 5db41f7f4d3..0d22d87df5b 100644 --- a/wpilibc/src/main/native/include/frc/simulation/UltrasonicSim.h +++ b/wpilibc/src/main/native/include/frc/simulation/UltrasonicSim.h @@ -36,14 +36,14 @@ class UltrasonicSim { /** * Sets if the range measurement is valid. * - * @param isValid True if valid + * @param valid True if valid */ - void SetRangeValid(bool isValid); + void SetRangeValid(bool valid); /** - * Sets the range measurement + * Sets the range measurement. * - * @param range The range + * @param range The range. */ void SetRange(units::inch_t range); diff --git a/wpilibc/src/main/native/include/frc/smartdashboard/FieldObject2d.h b/wpilibc/src/main/native/include/frc/smartdashboard/FieldObject2d.h index f55938e8df0..cf5d7d4ccc2 100644 --- a/wpilibc/src/main/native/include/frc/smartdashboard/FieldObject2d.h +++ b/wpilibc/src/main/native/include/frc/smartdashboard/FieldObject2d.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include diff --git a/wpilibc/src/main/native/include/frc/smartdashboard/SendableBuilderImpl.h b/wpilibc/src/main/native/include/frc/smartdashboard/SendableBuilderImpl.h index baee6ffaa45..2051d1b5d59 100644 --- a/wpilibc/src/main/native/include/frc/smartdashboard/SendableBuilderImpl.h +++ b/wpilibc/src/main/native/include/frc/smartdashboard/SendableBuilderImpl.h @@ -21,6 +21,9 @@ namespace frc { +/** + * Implementation detail for SendableBuilder. + */ class SendableBuilderImpl : public nt::NTSendableBuilder { public: SendableBuilderImpl() = default; diff --git a/wpilibc/src/main/native/include/frc/sysid/SysIdRoutineLog.h b/wpilibc/src/main/native/include/frc/sysid/SysIdRoutineLog.h new file mode 100644 index 00000000000..3c7544a6730 --- /dev/null +++ b/wpilibc/src/main/native/include/frc/sysid/SysIdRoutineLog.h @@ -0,0 +1,200 @@ +// 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace frc::sysid { + +/** + * Possible state of a SysId routine. + */ +enum class State { + /// Quasistatic forward test. + kQuasistaticForward, + /// Quasistatic reverse test. + kQuasistaticReverse, + /// Dynamic forward test. + kDynamicForward, + /// Dynamic reverse test. + kDynamicReverse, + /// No test. + kNone +}; + +/** + * Utility for logging data from a SysId test routine. Each complete routine + * (quasistatic and dynamic, forward and reverse) should have its own + * SysIdRoutineLog instance, with a unique log name. + */ +class SysIdRoutineLog { + using MotorEntries = wpi::StringMap; + using LogEntries = wpi::StringMap; + + public: + /** Logs data from a single motor during a SysIdRoutine. */ + class MotorLog { + public: + /** + * Log a generic data value from the motor. + * + * @param name The name of the data field being recorded. + * @param value The numeric value of the data field. + * @param unit The unit string of the data field. + * @return The motor log (for call chaining). + */ + MotorLog& value(std::string_view name, double value, std::string_view unit); + + /** + * Log the voltage applied to the motor. + * + * @param voltage The voltage to record. + * @return The motor log (for call chaining). + */ + MotorLog& voltage(units::volt_t voltage) { + return value("voltage", voltage.value(), voltage.name()); + } + + /** + * Log the linear position of the motor. + * + * @param position The linear position to record. + * @return The motor log (for call chaining). + */ + MotorLog& position(units::meter_t position) { + return value("position", position.value(), position.name()); + } + + /** + * Log the angular position of the motor. + * + * @param position The angular position to record. + * @return The motor log (for call chaining). + */ + MotorLog& position(units::turn_t position) { + return value("position", position.value(), position.name()); + } + + /** + * Log the linear velocity of the motor. + * + * @param velocity The linear velocity to record. + * @return The motor log (for call chaining). + */ + MotorLog& velocity(units::meters_per_second_t velocity) { + return value("velocity", velocity.value(), velocity.name()); + } + + /** + * Log the angular velocity of the motor. + * + * @param velocity The angular velocity to record. + * @return The motor log (for call chaining). + */ + MotorLog& velocity(units::turns_per_second_t velocity) { + return value("velocity", velocity.value(), velocity.name()); + } + + /** + * Log the linear acceleration of the motor. + * + * This is optional; SysId can perform an accurate fit without it. + * + * @param acceleration The linear acceleration to record. + * @return The motor log (for call chaining). + */ + MotorLog& acceleration(units::meters_per_second_squared_t acceleration) { + return value("acceleration", acceleration.value(), acceleration.name()); + } + + /** + * Log the angular acceleration of the motor. + * + * This is optional; SysId can perform an accurate fit without it. + * + * @param acceleration The angular acceleration to record. + * @return The motor log (for call chaining). + */ + MotorLog& acceleration(units::turns_per_second_squared_t acceleration) { + return value("acceleration", acceleration.value(), acceleration.name()); + } + + /** + * Log the current applied to the motor. + * + * This is optional; SysId can perform an accurate fit without it. + * + * @param current The current to record. + * @return The motor log (for call chaining). + */ + MotorLog& current(units::ampere_t current) { + return value("current", current.value(), current.name()); + } + + private: + friend class SysIdRoutineLog; + /** + * Create a new SysId motor log handle. + * + * @param motorName The name of the motor whose data is being logged. + * @param logName The name of the SysIdRoutineLog that this motor belongs + * to. + * @param logEntries The DataLog entries of the SysIdRoutineLog that this + * motor belongs to. + */ + MotorLog(std::string_view motorName, std::string_view logName, + LogEntries* logEntries); + std::string m_motorName; + std::string m_logName; + LogEntries* m_logEntries; + }; + + /** + * Create a new logging utility for a SysId test routine. + * + * @param logName The name for the test routine in the log. Should be unique + * between complete test routines (quasistatic and dynamic, forward and + * reverse). The current state of this test (e.g. "quasistatic-forward") + * will appear in WPILog under the "sysid-test-state-logName" entry. + */ + explicit SysIdRoutineLog(std::string_view logName); + + /** + * Records the current state of the SysId test routine. Should be called once + * per iteration during tests with the type of the current test, and once upon + * test end with state `none`. + * + * @param state The current state of the SysId test routine. + */ + void RecordState(State state); + + /** + * Log data from a motor during a SysId routine. + * + * @param motorName The name of the motor. + * @return Handle with chainable callbacks to log individual data fields. + */ + MotorLog Motor(std::string_view motorName); + + static std::string StateEnumToString(State state); + + private: + LogEntries m_logEntries; + std::string m_logName; + bool m_stateInitialized; + wpi::log::StringLogEntry m_state; +}; +} // namespace frc::sysid diff --git a/wpilibc/src/main/native/include/frc/util/Color.h b/wpilibc/src/main/native/include/frc/util/Color.h index 9e9228fe6d6..f51d3b12b75 100644 --- a/wpilibc/src/main/native/include/frc/util/Color.h +++ b/wpilibc/src/main/native/include/frc/util/Color.h @@ -864,8 +864,13 @@ class Color { wpi::hexdigit(b % 16)}}; } + /// Red component (0-1). double red = 0.0; + + /// Green component (0-1). double green = 0.0; + + /// Blue component (0-1). double blue = 0.0; private: diff --git a/wpilibc/src/main/native/include/frc/util/Color8Bit.h b/wpilibc/src/main/native/include/frc/util/Color8Bit.h index ce7915a5fd4..f4bc10406e9 100644 --- a/wpilibc/src/main/native/include/frc/util/Color8Bit.h +++ b/wpilibc/src/main/native/include/frc/util/Color8Bit.h @@ -115,8 +115,13 @@ class Color8Bit { wpi::hexdigit(blue / 16), wpi::hexdigit(blue % 16)}}; } + /// Red component (0-255). int red = 0; + + /// Green component (0-255). int green = 0; + + /// Blue component (0-255). int blue = 0; }; diff --git a/wpilibc/src/test/native/cpp/drive/DifferentialDriveTest.cpp b/wpilibc/src/test/native/cpp/drive/DifferentialDriveTest.cpp index f21f268f25b..9a22987ee02 100644 --- a/wpilibc/src/test/native/cpp/drive/DifferentialDriveTest.cpp +++ b/wpilibc/src/test/native/cpp/drive/DifferentialDriveTest.cpp @@ -5,7 +5,7 @@ #include #include "frc/drive/DifferentialDrive.h" -#include "motorcontrol/MockMotorController.h" +#include "motorcontrol/MockPWMMotorController.h" TEST(DifferentialDriveTest, ArcadeDriveIK) { // Forward @@ -240,9 +240,10 @@ TEST(DifferentialDriveTest, TankDriveIKSquared) { } TEST(DifferentialDriveTest, ArcadeDrive) { - frc::MockMotorController left; - frc::MockMotorController right; - frc::DifferentialDrive drive{left, right}; + frc::MockPWMMotorController left; + frc::MockPWMMotorController right; + frc::DifferentialDrive drive{[&](double output) { left.Set(output); }, + [&](double output) { right.Set(output); }}; drive.SetDeadband(0.0); // Forward @@ -277,9 +278,10 @@ TEST(DifferentialDriveTest, ArcadeDrive) { } TEST(DifferentialDriveTest, ArcadeDriveSquared) { - frc::MockMotorController left; - frc::MockMotorController right; - frc::DifferentialDrive drive{left, right}; + frc::MockPWMMotorController left; + frc::MockPWMMotorController right; + frc::DifferentialDrive drive{[&](double output) { left.Set(output); }, + [&](double output) { right.Set(output); }}; drive.SetDeadband(0.0); // Forward @@ -314,9 +316,10 @@ TEST(DifferentialDriveTest, ArcadeDriveSquared) { } TEST(DifferentialDriveTest, CurvatureDrive) { - frc::MockMotorController left; - frc::MockMotorController right; - frc::DifferentialDrive drive{left, right}; + frc::MockPWMMotorController left; + frc::MockPWMMotorController right; + frc::DifferentialDrive drive{[&](double output) { left.Set(output); }, + [&](double output) { right.Set(output); }}; drive.SetDeadband(0.0); // Forward @@ -351,9 +354,10 @@ TEST(DifferentialDriveTest, CurvatureDrive) { } TEST(DifferentialDriveTest, CurvatureDriveTurnInPlace) { - frc::MockMotorController left; - frc::MockMotorController right; - frc::DifferentialDrive drive{left, right}; + frc::MockPWMMotorController left; + frc::MockPWMMotorController right; + frc::DifferentialDrive drive{[&](double output) { left.Set(output); }, + [&](double output) { right.Set(output); }}; drive.SetDeadband(0.0); // Forward @@ -388,9 +392,10 @@ TEST(DifferentialDriveTest, CurvatureDriveTurnInPlace) { } TEST(DifferentialDriveTest, TankDrive) { - frc::MockMotorController left; - frc::MockMotorController right; - frc::DifferentialDrive drive{left, right}; + frc::MockPWMMotorController left; + frc::MockPWMMotorController right; + frc::DifferentialDrive drive{[&](double output) { left.Set(output); }, + [&](double output) { right.Set(output); }}; drive.SetDeadband(0.0); // Forward @@ -425,9 +430,10 @@ TEST(DifferentialDriveTest, TankDrive) { } TEST(DifferentialDriveTest, TankDriveSquared) { - frc::MockMotorController left; - frc::MockMotorController right; - frc::DifferentialDrive drive{left, right}; + frc::MockPWMMotorController left; + frc::MockPWMMotorController right; + frc::DifferentialDrive drive{[&](double output) { left.Set(output); }, + [&](double output) { right.Set(output); }}; drive.SetDeadband(0.0); // Forward diff --git a/wpilibc/src/test/native/cpp/drive/MecanumDriveTest.cpp b/wpilibc/src/test/native/cpp/drive/MecanumDriveTest.cpp index 1bbf464106c..b32b03c8246 100644 --- a/wpilibc/src/test/native/cpp/drive/MecanumDriveTest.cpp +++ b/wpilibc/src/test/native/cpp/drive/MecanumDriveTest.cpp @@ -5,7 +5,7 @@ #include #include "frc/drive/MecanumDrive.h" -#include "motorcontrol/MockMotorController.h" +#include "motorcontrol/MockPWMMotorController.h" TEST(MecanumDriveTest, CartesianIK) { // Forward @@ -82,11 +82,14 @@ TEST(MecanumDriveTest, CartesianIKGyro90CW) { } TEST(MecanumDriveTest, Cartesian) { - frc::MockMotorController fl; - frc::MockMotorController rl; - frc::MockMotorController fr; - frc::MockMotorController rr; - frc::MecanumDrive drive{fl, rl, fr, rr}; + frc::MockPWMMotorController fl; + frc::MockPWMMotorController rl; + frc::MockPWMMotorController fr; + frc::MockPWMMotorController rr; + frc::MecanumDrive drive{[&](double output) { fl.Set(output); }, + [&](double output) { rl.Set(output); }, + [&](double output) { fr.Set(output); }, + [&](double output) { rr.Set(output); }}; drive.SetDeadband(0.0); // Forward @@ -126,11 +129,14 @@ TEST(MecanumDriveTest, Cartesian) { } TEST(MecanumDriveTest, CartesianGyro90CW) { - frc::MockMotorController fl; - frc::MockMotorController fr; - frc::MockMotorController rl; - frc::MockMotorController rr; - frc::MecanumDrive drive{fl, rl, fr, rr}; + frc::MockPWMMotorController fl; + frc::MockPWMMotorController rl; + frc::MockPWMMotorController fr; + frc::MockPWMMotorController rr; + frc::MecanumDrive drive{[&](double output) { fl.Set(output); }, + [&](double output) { rl.Set(output); }, + [&](double output) { fr.Set(output); }, + [&](double output) { rr.Set(output); }}; drive.SetDeadband(0.0); // Forward in global frame; left in robot frame @@ -170,11 +176,14 @@ TEST(MecanumDriveTest, CartesianGyro90CW) { } TEST(MecanumDriveTest, Polar) { - frc::MockMotorController fl; - frc::MockMotorController fr; - frc::MockMotorController rl; - frc::MockMotorController rr; - frc::MecanumDrive drive{fl, rl, fr, rr}; + frc::MockPWMMotorController fl; + frc::MockPWMMotorController rl; + frc::MockPWMMotorController fr; + frc::MockPWMMotorController rr; + frc::MecanumDrive drive{[&](double output) { fl.Set(output); }, + [&](double output) { rl.Set(output); }, + [&](double output) { fr.Set(output); }, + [&](double output) { rr.Set(output); }}; drive.SetDeadband(0.0); // Forward diff --git a/wpilibc/src/test/native/cpp/event/EventLoopTest.cpp b/wpilibc/src/test/native/cpp/event/EventLoopTest.cpp new file mode 100644 index 00000000000..c903e0f8cde --- /dev/null +++ b/wpilibc/src/test/native/cpp/event/EventLoopTest.cpp @@ -0,0 +1,24 @@ +// 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 + +#include "frc/Errors.h" +#include "frc/event/EventLoop.h" + +using namespace frc; + +TEST(EventLoopTest, ConcurrentModification) { + EventLoop loop; + + loop.Bind([&loop] { ASSERT_THROW(loop.Bind([] {}), frc::RuntimeError); }); + + loop.Poll(); + + loop.Clear(); + + loop.Bind([&loop] { ASSERT_THROW(loop.Clear(), frc::RuntimeError); }); + + loop.Poll(); +} diff --git a/wpilibc/src/test/native/cpp/motorcontrol/MockPWMMotorController.cpp b/wpilibc/src/test/native/cpp/motorcontrol/MockPWMMotorController.cpp new file mode 100644 index 00000000000..7a51f33b9a1 --- /dev/null +++ b/wpilibc/src/test/native/cpp/motorcontrol/MockPWMMotorController.cpp @@ -0,0 +1,31 @@ +// 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 "motorcontrol/MockPWMMotorController.h" + +using namespace frc; + +void MockPWMMotorController::Set(double speed) { + m_speed = m_isInverted ? -speed : speed; +} + +double MockPWMMotorController::Get() const { + return m_speed; +} + +void MockPWMMotorController::SetInverted(bool isInverted) { + m_isInverted = isInverted; +} + +bool MockPWMMotorController::GetInverted() const { + return m_isInverted; +} + +void MockPWMMotorController::Disable() { + m_speed = 0; +} + +void MockPWMMotorController::StopMotor() { + Disable(); +} diff --git a/wpilibc/src/test/native/cpp/motorcontrol/MotorControllerGroupTest.cpp b/wpilibc/src/test/native/cpp/motorcontrol/MotorControllerGroupTest.cpp index 4ff889ad81e..740c46fe1a8 100644 --- a/wpilibc/src/test/native/cpp/motorcontrol/MotorControllerGroupTest.cpp +++ b/wpilibc/src/test/native/cpp/motorcontrol/MotorControllerGroupTest.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "motorcontrol/MockMotorController.h" @@ -32,6 +33,8 @@ std::ostream& operator<<(std::ostream& os, return os; } +WPI_IGNORE_DEPRECATED + /** * A fixture used for MotorControllerGroup testing. */ @@ -124,3 +127,5 @@ TEST_P(MotorControllerGroupTest, StopMotor) { INSTANTIATE_TEST_SUITE_P(Tests, MotorControllerGroupTest, testing::Values(TEST_ONE, TEST_TWO, TEST_THREE)); + +WPI_UNIGNORE_DEPRECATED diff --git a/wpilibc/src/test/native/cpp/simulation/DriverStationSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/DriverStationSimTest.cpp index 2513252893f..8a5941ad377 100644 --- a/wpilibc/src/test/native/cpp/simulation/DriverStationSimTest.cpp +++ b/wpilibc/src/test/native/cpp/simulation/DriverStationSimTest.cpp @@ -104,15 +104,19 @@ TEST(DriverStationTest, FmsAttached) { TEST(DriverStationTest, DsAttached) { HAL_Initialize(500, 0); DriverStationSim::ResetData(); + DriverStation::RefreshData(); + EXPECT_FALSE(DriverStationSim::GetDsAttached()); + EXPECT_FALSE(DriverStation::IsDSAttached()); DriverStationSim::NotifyNewData(); + EXPECT_TRUE(DriverStationSim::GetDsAttached()); EXPECT_TRUE(DriverStation::IsDSAttached()); BooleanCallback callback; auto cb = DriverStationSim::RegisterDsAttachedCallback(callback.GetCallback(), false); DriverStationSim::SetDsAttached(false); - DriverStationSim::NotifyNewData(); + DriverStation::RefreshData(); EXPECT_FALSE(DriverStationSim::GetDsAttached()); EXPECT_FALSE(DriverStation::IsDSAttached()); EXPECT_TRUE(callback.WasTriggered()); diff --git a/wpilibc/src/test/native/include/motorcontrol/MockMotorController.h b/wpilibc/src/test/native/include/motorcontrol/MockMotorController.h index e17931fbb69..aab6ce47492 100644 --- a/wpilibc/src/test/native/include/motorcontrol/MockMotorController.h +++ b/wpilibc/src/test/native/include/motorcontrol/MockMotorController.h @@ -4,10 +4,14 @@ #pragma once +#include + #include "frc/motorcontrol/MotorController.h" namespace frc { +WPI_IGNORE_DEPRECATED + class MockMotorController : public MotorController { public: void Set(double speed) override; @@ -22,4 +26,6 @@ class MockMotorController : public MotorController { bool m_isInverted = false; }; +WPI_UNIGNORE_DEPRECATED + } // namespace frc diff --git a/wpilibc/src/test/native/include/motorcontrol/MockPWMMotorController.h b/wpilibc/src/test/native/include/motorcontrol/MockPWMMotorController.h new file mode 100644 index 00000000000..18bd1f56159 --- /dev/null +++ b/wpilibc/src/test/native/include/motorcontrol/MockPWMMotorController.h @@ -0,0 +1,23 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#pragma once + +namespace frc { + +class MockPWMMotorController { + public: + void Set(double speed); + double Get() const; + void SetInverted(bool isInverted); + bool GetInverted() const; + void Disable(); + void StopMotor(); + + private: + double m_speed = 0.0; + bool m_isInverted = false; +}; + +} // namespace frc diff --git a/wpilibcExamples/src/main/cpp/examples/AprilTagsVision/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/AprilTagsVision/cpp/Robot.cpp index 0ed53fd8536..8df29339fa6 100644 --- a/wpilibcExamples/src/main/cpp/examples/AprilTagsVision/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/AprilTagsVision/cpp/Robot.cpp @@ -37,13 +37,13 @@ class Robot : public frc::TimedRobot { private: static void VisionThread() { frc::AprilTagDetector detector; - // look for tag16h5, don't correct any error bits - detector.AddFamily("tag16h5", 0); + // look for tag36h11, correct 3 error bits + detector.AddFamily("tag36h11", 3); // Set up Pose Estimator - parameters are for a Microsoft Lifecam HD-3000 // (https://www.chiefdelphi.com/t/wpilib-apriltagdetector-sample-code/421411/21) frc::AprilTagPoseEstimator::Config poseEstConfig = { - .tagSize = units::length::inch_t(6.0), + .tagSize = units::length::inch_t(6.5), .fx = 699.3778103158814, .fy = 677.7161226393544, .cx = 345.6059345433618, diff --git a/wpilibcExamples/src/main/cpp/examples/ArcadeDrive/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/ArcadeDrive/cpp/Robot.cpp index b636b44b47f..9c8d64064fa 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArcadeDrive/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/ArcadeDrive/cpp/Robot.cpp @@ -14,10 +14,17 @@ class Robot : public frc::TimedRobot { frc::PWMSparkMax m_leftMotor{0}; frc::PWMSparkMax m_rightMotor{1}; - frc::DifferentialDrive m_robotDrive{m_leftMotor, m_rightMotor}; + frc::DifferentialDrive m_robotDrive{ + [&](double output) { m_leftMotor.Set(output); }, + [&](double output) { m_rightMotor.Set(output); }}; frc::Joystick m_stick{0}; public: + Robot() { + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_leftMotor); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_rightMotor); + } + void RobotInit() override { // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's diff --git a/wpilibcExamples/src/main/cpp/examples/ArcadeDriveXboxController/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/ArcadeDriveXboxController/cpp/Robot.cpp index 23c9a569dd1..38ff862ab96 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArcadeDriveXboxController/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/ArcadeDriveXboxController/cpp/Robot.cpp @@ -14,10 +14,17 @@ class Robot : public frc::TimedRobot { frc::PWMSparkMax m_leftMotor{0}; frc::PWMSparkMax m_rightMotor{1}; - frc::DifferentialDrive m_robotDrive{m_leftMotor, m_rightMotor}; + frc::DifferentialDrive m_robotDrive{ + [&](double output) { m_leftMotor.Set(output); }, + [&](double output) { m_rightMotor.Set(output); }}; frc::XboxController m_driverController{0}; public: + Robot() { + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_leftMotor); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_rightMotor); + } + void RobotInit() override { // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's diff --git a/wpilibcExamples/src/main/cpp/examples/ArmBot/cpp/subsystems/DriveSubsystem.cpp b/wpilibcExamples/src/main/cpp/examples/ArmBot/cpp/subsystems/DriveSubsystem.cpp index aeeac3ddf90..13522950d80 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArmBot/cpp/subsystems/DriveSubsystem.cpp +++ b/wpilibcExamples/src/main/cpp/examples/ArmBot/cpp/subsystems/DriveSubsystem.cpp @@ -13,13 +13,20 @@ DriveSubsystem::DriveSubsystem() m_right2{kRightMotor2Port}, m_leftEncoder{kLeftEncoderPorts[0], kLeftEncoderPorts[1]}, m_rightEncoder{kRightEncoderPorts[0], kRightEncoderPorts[1]} { + wpi::SendableRegistry::AddChild(&m_drive, &m_left1); + wpi::SendableRegistry::AddChild(&m_drive, &m_right1); + + m_left1.AddFollower(m_left2); + m_right1.AddFollower(m_right2); + // Set the distance per pulse for the encoders m_leftEncoder.SetDistancePerPulse(kEncoderDistancePerPulse); m_rightEncoder.SetDistancePerPulse(kEncoderDistancePerPulse); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.SetInverted(true); + m_right1.SetInverted(true); } void DriveSubsystem::Periodic() { diff --git a/wpilibcExamples/src/main/cpp/examples/ArmBot/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/ArmBot/include/subsystems/DriveSubsystem.h index 47bf28e4d91..47d3f3d49b2 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArmBot/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/ArmBot/include/subsystems/DriveSubsystem.h @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -75,14 +74,9 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right1; frc::PWMSparkMax m_right2; - // The motors on the left side of the drive - frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; - - // The motors on the right side of the drive - frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; - // The robot's drive - frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; + frc::DifferentialDrive m_drive{[&](double output) { m_left1.Set(output); }, + [&](double output) { m_right1.Set(output); }}; // The left-side drive encoder frc::Encoder m_leftEncoder; diff --git a/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/cpp/subsystems/DriveSubsystem.cpp b/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/cpp/subsystems/DriveSubsystem.cpp index 16409ad7a77..236d4682357 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/cpp/subsystems/DriveSubsystem.cpp +++ b/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/cpp/subsystems/DriveSubsystem.cpp @@ -13,13 +13,20 @@ DriveSubsystem::DriveSubsystem() m_right2{kRightMotor2Port}, m_leftEncoder{kLeftEncoderPorts[0], kLeftEncoderPorts[1]}, m_rightEncoder{kRightEncoderPorts[0], kRightEncoderPorts[1]} { + wpi::SendableRegistry::AddChild(&m_drive, &m_left1); + wpi::SendableRegistry::AddChild(&m_drive, &m_right1); + + m_left1.AddFollower(m_left2); + m_right1.AddFollower(m_right2); + // Set the distance per pulse for the encoders m_leftEncoder.SetDistancePerPulse(kEncoderDistancePerPulse); m_rightEncoder.SetDistancePerPulse(kEncoderDistancePerPulse); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.SetInverted(true); + m_right1.SetInverted(true); } void DriveSubsystem::Periodic() { diff --git a/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/ExampleSmartMotorController.h b/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/ExampleSmartMotorController.h index 5d55839892f..bd48d88e0bc 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/ExampleSmartMotorController.h +++ b/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/ExampleSmartMotorController.h @@ -4,15 +4,13 @@ #pragma once -#include - /** * A simplified stub class that simulates the API of a common "smart" motor * controller. * *

Has no actual functionality. */ -class ExampleSmartMotorController : public frc::MotorController { +class ExampleSmartMotorController { public: enum PIDMode { kPosition, kVelocity, kMovementWitchcraft }; @@ -68,15 +66,15 @@ class ExampleSmartMotorController : public frc::MotorController { */ void ResetEncoder() {} - void Set(double speed) override {} + void Set(double speed) {} - double Get() const override { return 0; } + double Get() const { return 0; } - void SetInverted(bool isInverted) override {} + void SetInverted(bool isInverted) {} - bool GetInverted() const override { return false; } + bool GetInverted() const { return false; } - void Disable() override {} + void Disable() {} - void StopMotor() override {} + void StopMotor() {} }; diff --git a/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/subsystems/DriveSubsystem.h index 6830b960d09..ca7e28e8293 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/subsystems/DriveSubsystem.h @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -73,14 +72,9 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right1; frc::PWMSparkMax m_right2; - // The motors on the left side of the drive - frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; - - // The motors on the right side of the drive - frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; - // The robot's drive - frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; + frc::DifferentialDrive m_drive{[&](double output) { m_left1.Set(output); }, + [&](double output) { m_right1.Set(output); }}; // The left-side drive encoder frc::Encoder m_leftEncoder; diff --git a/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/cpp/Drivetrain.cpp b/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/cpp/Drivetrain.cpp index cc7db7b3bab..9470a7ad3aa 100644 --- a/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/cpp/Drivetrain.cpp +++ b/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/cpp/Drivetrain.cpp @@ -12,8 +12,8 @@ void Drivetrain::SetSpeeds(const frc::DifferentialDriveWheelSpeeds& speeds) { const double rightOutput = m_rightPIDController.Calculate( m_rightEncoder.GetRate(), speeds.right.value()); - m_leftGroup.SetVoltage(units::volt_t{leftOutput} + leftFeedforward); - m_rightGroup.SetVoltage(units::volt_t{rightOutput} + rightFeedforward); + m_leftLeader.SetVoltage(units::volt_t{leftOutput} + leftFeedforward); + m_rightLeader.SetVoltage(units::volt_t{rightOutput} + rightFeedforward); } void Drivetrain::Drive(units::meters_per_second_t xSpeed, diff --git a/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/include/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/include/Drivetrain.h index 54b2e26290f..85bccc7f521 100644 --- a/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/include/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/include/Drivetrain.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -25,10 +24,13 @@ class Drivetrain { public: Drivetrain() { + m_leftLeader.AddFollower(m_leftFollower); + m_rightLeader.AddFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightGroup.SetInverted(true); + m_rightLeader.SetInverted(true); m_gyro.Reset(); // Set the distance per pulse for the drive encoders. We can simply use the @@ -63,9 +65,6 @@ class Drivetrain { frc::PWMSparkMax m_rightLeader{3}; frc::PWMSparkMax m_rightFollower{4}; - frc::MotorControllerGroup m_leftGroup{m_leftLeader, m_leftFollower}; - frc::MotorControllerGroup m_rightGroup{m_rightLeader, m_rightFollower}; - frc::Encoder m_leftEncoder{0, 1}; frc::Encoder m_rightEncoder{2, 3}; diff --git a/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Drivetrain.cpp b/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Drivetrain.cpp index 725074a71a7..caba17d9e93 100644 --- a/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Drivetrain.cpp +++ b/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Drivetrain.cpp @@ -7,10 +7,13 @@ #include "ExampleGlobalMeasurementSensor.h" Drivetrain::Drivetrain() { + m_leftLeader.AddFollower(m_leftFollower); + m_rightLeader.AddFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightGroup.SetInverted(true); + m_rightLeader.SetInverted(true); m_gyro.Reset(); @@ -37,8 +40,8 @@ void Drivetrain::SetSpeeds(const frc::DifferentialDriveWheelSpeeds& speeds) { const double rightOutput = m_rightPIDController.Calculate( m_rightEncoder.GetRate(), speeds.right.value()); - m_leftGroup.SetVoltage(units::volt_t{leftOutput} + leftFeedforward); - m_rightGroup.SetVoltage(units::volt_t{rightOutput} + rightFeedforward); + m_leftLeader.SetVoltage(units::volt_t{leftOutput} + leftFeedforward); + m_rightLeader.SetVoltage(units::volt_t{rightOutput} + rightFeedforward); } void Drivetrain::Drive(units::meters_per_second_t xSpeed, @@ -110,9 +113,9 @@ void Drivetrain::SimulationPeriodic() { // To update our simulation, we set motor voltage inputs, update the // simulation, and write the simulated positions and velocities to our // simulated encoder and gyro. - m_drivetrainSimulator.SetInputs(units::volt_t{m_leftGroup.Get()} * + m_drivetrainSimulator.SetInputs(units::volt_t{m_leftLeader.Get()} * frc::RobotController::GetInputVoltage(), - units::volt_t{m_rightGroup.Get()} * + units::volt_t{m_rightLeader.Get()} * frc::RobotController::GetInputVoltage()); m_drivetrainSimulator.Update(20_ms); diff --git a/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h index ac4de4c566f..63a9aea0142 100644 --- a/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -140,9 +139,6 @@ class Drivetrain { frc::PWMSparkMax m_rightLeader{3}; frc::PWMSparkMax m_rightFollower{4}; - frc::MotorControllerGroup m_leftGroup{m_leftLeader, m_leftFollower}; - frc::MotorControllerGroup m_rightGroup{m_rightLeader, m_rightFollower}; - frc::Encoder m_leftEncoder{0, 1}; frc::Encoder m_rightEncoder{2, 3}; diff --git a/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/subsystems/DriveSubsystem.cpp b/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/subsystems/DriveSubsystem.cpp index db8ba707551..84fae4f2f0e 100644 --- a/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/subsystems/DriveSubsystem.cpp +++ b/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/subsystems/DriveSubsystem.cpp @@ -12,16 +12,14 @@ DriveSubsystem::DriveSubsystem() m_rightLeader{kRightMotor1Port}, m_rightFollower{kRightMotor2Port}, m_feedforward{ks, kv, ka} { + wpi::SendableRegistry::AddChild(&m_drive, &m_leftLeader); + wpi::SendableRegistry::AddChild(&m_drive, &m_rightLeader); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. m_rightLeader.SetInverted(true); - // You might need to not do this depending on the specific motor controller - // that you are using -- contact the respective vendor's documentation for - // more details. - m_rightFollower.SetInverted(true); - m_leftFollower.Follow(m_leftLeader); m_rightFollower.Follow(m_rightLeader); diff --git a/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/ExampleSmartMotorController.h b/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/ExampleSmartMotorController.h index 71dc4d4864a..fe09a15f226 100644 --- a/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/ExampleSmartMotorController.h +++ b/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/ExampleSmartMotorController.h @@ -4,15 +4,13 @@ #pragma once -#include - /** * A simplified stub class that simulates the API of a common "smart" motor * controller. * *

Has no actual functionality. */ -class ExampleSmartMotorController : public frc::MotorController { +class ExampleSmartMotorController { public: enum PIDMode { kPosition, kVelocity, kMovementWitchcraft }; @@ -68,17 +66,17 @@ class ExampleSmartMotorController : public frc::MotorController { */ void ResetEncoder() {} - void Set(double speed) override { m_value = speed; } + void Set(double speed) { m_value = speed; } - double Get() const override { return m_value; } + double Get() const { return m_value; } - void SetInverted(bool isInverted) override {} + void SetInverted(bool isInverted) {} - bool GetInverted() const override { return false; } + bool GetInverted() const { return false; } - void Disable() override {} + void Disable() {} - void StopMotor() override {} + void StopMotor() {} private: double m_value = 0.0; diff --git a/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/subsystems/DriveSubsystem.h index 9086353bf94..3dd5f03f10a 100644 --- a/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/subsystems/DriveSubsystem.h @@ -83,5 +83,7 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::SimpleMotorFeedforward m_feedforward; // The robot's drive - frc::DifferentialDrive m_drive{m_leftLeader, m_rightLeader}; + frc::DifferentialDrive m_drive{ + [&](double output) { m_leftLeader.Set(output); }, + [&](double output) { m_rightLeader.Set(output); }}; }; diff --git a/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialProfile/include/ExampleSmartMotorController.h b/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialProfile/include/ExampleSmartMotorController.h index 5d55839892f..bd48d88e0bc 100644 --- a/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialProfile/include/ExampleSmartMotorController.h +++ b/wpilibcExamples/src/main/cpp/examples/ElevatorExponentialProfile/include/ExampleSmartMotorController.h @@ -4,15 +4,13 @@ #pragma once -#include - /** * A simplified stub class that simulates the API of a common "smart" motor * controller. * *

Has no actual functionality. */ -class ExampleSmartMotorController : public frc::MotorController { +class ExampleSmartMotorController { public: enum PIDMode { kPosition, kVelocity, kMovementWitchcraft }; @@ -68,15 +66,15 @@ class ExampleSmartMotorController : public frc::MotorController { */ void ResetEncoder() {} - void Set(double speed) override {} + void Set(double speed) {} - double Get() const override { return 0; } + double Get() const { return 0; } - void SetInverted(bool isInverted) override {} + void SetInverted(bool isInverted) {} - bool GetInverted() const override { return false; } + bool GetInverted() const { return false; } - void Disable() override {} + void Disable() {} - void StopMotor() override {} + void StopMotor() {} }; diff --git a/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/include/ExampleSmartMotorController.h b/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/include/ExampleSmartMotorController.h index 5d55839892f..bd48d88e0bc 100644 --- a/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/include/ExampleSmartMotorController.h +++ b/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/include/ExampleSmartMotorController.h @@ -4,15 +4,13 @@ #pragma once -#include - /** * A simplified stub class that simulates the API of a common "smart" motor * controller. * *

Has no actual functionality. */ -class ExampleSmartMotorController : public frc::MotorController { +class ExampleSmartMotorController { public: enum PIDMode { kPosition, kVelocity, kMovementWitchcraft }; @@ -68,15 +66,15 @@ class ExampleSmartMotorController : public frc::MotorController { */ void ResetEncoder() {} - void Set(double speed) override {} + void Set(double speed) {} - double Get() const override { return 0; } + double Get() const { return 0; } - void SetInverted(bool isInverted) override {} + void SetInverted(bool isInverted) {} - bool GetInverted() const override { return false; } + bool GetInverted() const { return false; } - void Disable() override {} + void Disable() {} - void StopMotor() override {} + void StopMotor() {} }; diff --git a/wpilibcExamples/src/main/cpp/examples/Frisbeebot/cpp/subsystems/DriveSubsystem.cpp b/wpilibcExamples/src/main/cpp/examples/Frisbeebot/cpp/subsystems/DriveSubsystem.cpp index f40a649ec84..0e4068f22eb 100644 --- a/wpilibcExamples/src/main/cpp/examples/Frisbeebot/cpp/subsystems/DriveSubsystem.cpp +++ b/wpilibcExamples/src/main/cpp/examples/Frisbeebot/cpp/subsystems/DriveSubsystem.cpp @@ -13,10 +13,16 @@ DriveSubsystem::DriveSubsystem() m_right2{kRightMotor2Port}, m_leftEncoder{kLeftEncoderPorts[0], kLeftEncoderPorts[1]}, m_rightEncoder{kRightEncoderPorts[0], kRightEncoderPorts[1]} { + wpi::SendableRegistry::AddChild(&m_drive, &m_left1); + wpi::SendableRegistry::AddChild(&m_drive, &m_right1); + + m_left1.AddFollower(m_left2); + m_right1.AddFollower(m_right2); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_leftMotors.SetInverted(true); + m_left1.SetInverted(true); // Set the distance per pulse for the encoders m_leftEncoder.SetDistancePerPulse(kEncoderDistancePerPulse); diff --git a/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/subsystems/DriveSubsystem.h index 47bf28e4d91..47d3f3d49b2 100644 --- a/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/subsystems/DriveSubsystem.h @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -75,14 +74,9 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right1; frc::PWMSparkMax m_right2; - // The motors on the left side of the drive - frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; - - // The motors on the right side of the drive - frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; - // The robot's drive - frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; + frc::DifferentialDrive m_drive{[&](double output) { m_left1.Set(output); }, + [&](double output) { m_right1.Set(output); }}; // The left-side drive encoder frc::Encoder m_leftEncoder; diff --git a/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/subsystems/Drivetrain.cpp b/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/subsystems/Drivetrain.cpp index 2bfd391d6aa..2928036afc9 100644 --- a/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/subsystems/Drivetrain.cpp +++ b/wpilibcExamples/src/main/cpp/examples/GearsBot/cpp/subsystems/Drivetrain.cpp @@ -11,10 +11,16 @@ #include Drivetrain::Drivetrain() { + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_frontLeft); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_frontRight); + + m_frontLeft.AddFollower(m_rearLeft); + m_frontRight.AddFollower(m_rearRight); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_right.SetInverted(true); + m_frontRight.SetInverted(true); // Encoders may measure differently in the real world and in // simulation. In this example the robot moves 0.042 barleycorns diff --git a/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Drivetrain.h index 9e739c4dbc3..cc3c95f9b3a 100644 --- a/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Drivetrain.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -66,13 +65,13 @@ class Drivetrain : public frc2::SubsystemBase { private: frc::PWMSparkMax m_frontLeft{1}; frc::PWMSparkMax m_rearLeft{2}; - frc::MotorControllerGroup m_left{m_frontLeft, m_rearLeft}; frc::PWMSparkMax m_frontRight{3}; frc::PWMSparkMax m_rearRight{4}; - frc::MotorControllerGroup m_right{m_frontRight, m_rearRight}; - frc::DifferentialDrive m_robotDrive{m_left, m_right}; + frc::DifferentialDrive m_robotDrive{ + [&](double output) { m_frontLeft.Set(output); }, + [&](double output) { m_frontRight.Set(output); }}; frc::Encoder m_leftEncoder{1, 2}; frc::Encoder m_rightEncoder{3, 4}; diff --git a/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp index 01b72108759..0a73f1402f8 100644 --- a/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp @@ -11,6 +11,9 @@ class Robot : public frc::TimedRobot { public: Robot() { + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_left); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_right); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. @@ -48,7 +51,9 @@ class Robot : public frc::TimedRobot { // Robot drive system frc::PWMSparkMax m_left{0}; frc::PWMSparkMax m_right{1}; - frc::DifferentialDrive m_robotDrive{m_left, m_right}; + frc::DifferentialDrive m_robotDrive{ + [&](double output) { m_left.Set(output); }, + [&](double output) { m_right.Set(output); }}; frc::XboxController m_controller{0}; frc::Timer m_timer; diff --git a/wpilibcExamples/src/main/cpp/examples/Gyro/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/Gyro/cpp/Robot.cpp index 5230c7c3973..d8af4100acb 100644 --- a/wpilibcExamples/src/main/cpp/examples/Gyro/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/Gyro/cpp/Robot.cpp @@ -17,6 +17,11 @@ */ class Robot : public frc::TimedRobot { public: + Robot() { + wpi::SendableRegistry::AddChild(&m_drive, &m_left); + wpi::SendableRegistry::AddChild(&m_drive, &m_right); + } + void RobotInit() override { m_gyro.SetSensitivity(kVoltsPerDegreePerSecond); // We need to invert one side of the drivetrain so that positive voltages @@ -50,7 +55,8 @@ class Robot : public frc::TimedRobot { frc::PWMSparkMax m_left{kLeftMotorPort}; frc::PWMSparkMax m_right{kRightMotorPort}; - frc::DifferentialDrive m_drive{m_left, m_right}; + frc::DifferentialDrive m_drive{[&](double output) { m_left.Set(output); }, + [&](double output) { m_right.Set(output); }}; frc::AnalogGyro m_gyro{kGyroPort}; frc::Joystick m_joystick{kJoystickPort}; diff --git a/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/subsystems/DriveSubsystem.cpp b/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/subsystems/DriveSubsystem.cpp index 0cbd0e548d1..a97a5c071f6 100644 --- a/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/subsystems/DriveSubsystem.cpp +++ b/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/subsystems/DriveSubsystem.cpp @@ -13,10 +13,16 @@ DriveSubsystem::DriveSubsystem() m_right2{kRightMotor2Port}, m_leftEncoder{kLeftEncoderPorts[0], kLeftEncoderPorts[1]}, m_rightEncoder{kRightEncoderPorts[0], kRightEncoderPorts[1]} { + wpi::SendableRegistry::AddChild(&m_drive, &m_left1); + wpi::SendableRegistry::AddChild(&m_drive, &m_right1); + + m_left1.AddFollower(m_left2); + m_right1.AddFollower(m_right2); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.SetInverted(true); + m_right1.SetInverted(true); // Set the distance per pulse for the encoders m_leftEncoder.SetDistancePerPulse(kEncoderDistancePerPulse); diff --git a/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/include/subsystems/DriveSubsystem.h index 96174dd4f7a..d7cce6c05c2 100644 --- a/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/include/subsystems/DriveSubsystem.h @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -91,14 +90,9 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right1; frc::PWMSparkMax m_right2; - // The motors on the left side of the drive - frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; - - // The motors on the right side of the drive - frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; - // The robot's drive - frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; + frc::DifferentialDrive m_drive{[&](double output) { m_left1.Set(output); }, + [&](double output) { m_right1.Set(output); }}; // The left-side drive encoder frc::Encoder m_leftEncoder; diff --git a/wpilibcExamples/src/main/cpp/examples/GyroMecanum/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/GyroMecanum/cpp/Robot.cpp index 2207f79de51..7c589fd268e 100644 --- a/wpilibcExamples/src/main/cpp/examples/GyroMecanum/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/GyroMecanum/cpp/Robot.cpp @@ -16,6 +16,11 @@ class Robot : public frc::TimedRobot { public: void RobotInit() override { + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_frontLeft); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_rearLeft); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_frontRight); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_rearRight); + // Invert the right side motors. You may need to change or remove this to // match your robot. m_frontRight.SetInverted(true); @@ -48,8 +53,11 @@ class Robot : public frc::TimedRobot { frc::PWMSparkMax m_rearLeft{kRearLeftMotorPort}; frc::PWMSparkMax m_frontRight{kFrontRightMotorPort}; frc::PWMSparkMax m_rearRight{kRearRightMotorPort}; - frc::MecanumDrive m_robotDrive{m_frontLeft, m_rearLeft, m_frontRight, - m_rearRight}; + frc::MecanumDrive m_robotDrive{ + [&](double output) { m_frontLeft.Set(output); }, + [&](double output) { m_rearLeft.Set(output); }, + [&](double output) { m_frontRight.Set(output); }, + [&](double output) { m_rearRight.Set(output); }}; frc::AnalogGyro m_gyro{kGyroPort}; frc::Joystick m_joystick{kJoystickPort}; diff --git a/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/DriveSubsystem.cpp b/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/DriveSubsystem.cpp index 3372a4dca9d..d7b33eac79d 100644 --- a/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/DriveSubsystem.cpp +++ b/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/DriveSubsystem.cpp @@ -15,10 +15,16 @@ DriveSubsystem::DriveSubsystem() m_right2{kRightMotor2Port}, m_leftEncoder{kLeftEncoderPorts[0], kLeftEncoderPorts[1]}, m_rightEncoder{kRightEncoderPorts[0], kRightEncoderPorts[1]} { + wpi::SendableRegistry::AddChild(&m_drive, &m_left1); + wpi::SendableRegistry::AddChild(&m_drive, &m_right1); + + m_left1.AddFollower(m_left2); + m_right1.AddFollower(m_right2); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.SetInverted(true); + m_right1.SetInverted(true); // Set the distance per pulse for the encoders m_leftEncoder.SetDistancePerPulse(kEncoderDistancePerPulse); diff --git a/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/DriveSubsystem.h index 5984a1a4f38..6cab5803884 100644 --- a/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/DriveSubsystem.h @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -63,14 +62,9 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right1; frc::PWMSparkMax m_right2; - // The motors on the left side of the drive - frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; - - // The motors on the right side of the drive - frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; - // The robot's drive - frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; + frc::DifferentialDrive m_drive{[&](double output) { m_left1.Set(output); }, + [&](double output) { m_right1.Set(output); }}; // The left-side drive encoder frc::Encoder m_leftEncoder; diff --git a/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/DriveSubsystem.cpp b/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/DriveSubsystem.cpp index 3372a4dca9d..d7b33eac79d 100644 --- a/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/DriveSubsystem.cpp +++ b/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/DriveSubsystem.cpp @@ -15,10 +15,16 @@ DriveSubsystem::DriveSubsystem() m_right2{kRightMotor2Port}, m_leftEncoder{kLeftEncoderPorts[0], kLeftEncoderPorts[1]}, m_rightEncoder{kRightEncoderPorts[0], kRightEncoderPorts[1]} { + wpi::SendableRegistry::AddChild(&m_drive, &m_left1); + wpi::SendableRegistry::AddChild(&m_drive, &m_right1); + + m_left1.AddFollower(m_left2); + m_right1.AddFollower(m_right2); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.SetInverted(true); + m_right1.SetInverted(true); // Set the distance per pulse for the encoders m_leftEncoder.SetDistancePerPulse(kEncoderDistancePerPulse); diff --git a/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/DriveSubsystem.h index 5984a1a4f38..6cab5803884 100644 --- a/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/DriveSubsystem.h @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -63,14 +62,9 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right1; frc::PWMSparkMax m_right2; - // The motors on the left side of the drive - frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; - - // The motors on the right side of the drive - frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; - // The robot's drive - frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; + frc::DifferentialDrive m_drive{[&](double output) { m_left1.Set(output); }, + [&](double output) { m_right1.Set(output); }}; // The left-side drive encoder frc::Encoder m_leftEncoder; diff --git a/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/RobotContainer.cpp b/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/RobotContainer.cpp index 856ec999119..0d3db36cb04 100644 --- a/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/RobotContainer.cpp +++ b/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/RobotContainer.cpp @@ -111,8 +111,8 @@ frc2::CommandPtr RobotContainer::GetAutonomousCommand() { // command, then stop at the end. return frc2::cmd::Sequence( frc2::InstantCommand( - [this, &exampleTrajectory]() { - m_drive.ResetOdometry(exampleTrajectory.InitialPose()); + [this, initialPose = exampleTrajectory.InitialPose()]() { + m_drive.ResetOdometry(initialPose); }, {}) .ToPtr(), diff --git a/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/subsystems/DriveSubsystem.cpp b/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/subsystems/DriveSubsystem.cpp index 292ad1f52c9..8a55821e71b 100644 --- a/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/subsystems/DriveSubsystem.cpp +++ b/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/subsystems/DriveSubsystem.cpp @@ -30,6 +30,11 @@ DriveSubsystem::DriveSubsystem() m_odometry{kDriveKinematics, m_gyro.GetRotation2d(), getCurrentWheelDistances(), frc::Pose2d{}} { + wpi::SendableRegistry::AddChild(&m_drive, &m_frontLeft); + wpi::SendableRegistry::AddChild(&m_drive, &m_rearLeft); + wpi::SendableRegistry::AddChild(&m_drive, &m_frontRight); + wpi::SendableRegistry::AddChild(&m_drive, &m_rearRight); + // Set the distance per pulse for the encoders m_frontLeftEncoder.SetDistancePerPulse(kEncoderDistancePerPulse); m_rearLeftEncoder.SetDistancePerPulse(kEncoderDistancePerPulse); diff --git a/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/include/subsystems/DriveSubsystem.h index 579a3950eaa..7efb2250874 100644 --- a/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/include/subsystems/DriveSubsystem.h @@ -148,7 +148,10 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_rearRight; // The robot's drive - frc::MecanumDrive m_drive{m_frontLeft, m_rearLeft, m_frontRight, m_rearRight}; + frc::MecanumDrive m_drive{[&](double output) { m_frontLeft.Set(output); }, + [&](double output) { m_rearLeft.Set(output); }, + [&](double output) { m_frontRight.Set(output); }, + [&](double output) { m_rearRight.Set(output); }}; // The front-left-side drive encoder frc::Encoder m_frontLeftEncoder; diff --git a/wpilibcExamples/src/main/cpp/examples/MecanumDrive/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/MecanumDrive/cpp/Robot.cpp index 8d9d7aebf75..eeb9ce13233 100644 --- a/wpilibcExamples/src/main/cpp/examples/MecanumDrive/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/MecanumDrive/cpp/Robot.cpp @@ -14,6 +14,11 @@ class Robot : public frc::TimedRobot { public: void RobotInit() override { + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_frontLeft); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_rearLeft); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_frontRight); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_rearRight); + // Invert the right side motors. You may need to change or remove this to // match your robot. m_frontRight.SetInverted(true); @@ -40,8 +45,11 @@ class Robot : public frc::TimedRobot { frc::PWMSparkMax m_rearLeft{kRearLeftChannel}; frc::PWMSparkMax m_frontRight{kFrontRightChannel}; frc::PWMSparkMax m_rearRight{kRearRightChannel}; - frc::MecanumDrive m_robotDrive{m_frontLeft, m_rearLeft, m_frontRight, - m_rearRight}; + frc::MecanumDrive m_robotDrive{ + [&](double output) { m_frontLeft.Set(output); }, + [&](double output) { m_rearLeft.Set(output); }, + [&](double output) { m_frontRight.Set(output); }, + [&](double output) { m_rearRight.Set(output); }}; frc::Joystick m_stick{kJoystickChannel}; }; diff --git a/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/RobotContainer.cpp b/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/RobotContainer.cpp index 5fa8c62011c..feacbcc2a7f 100644 --- a/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/RobotContainer.cpp +++ b/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/RobotContainer.cpp @@ -84,8 +84,8 @@ frc2::CommandPtr RobotContainer::GetAutonomousCommand() { // Reset odometry to the initial pose of the trajectory, run path following // command, then stop at the end. return frc2::cmd::RunOnce( - [this, &exampleTrajectory] { - m_drive.ResetOdometry(exampleTrajectory.InitialPose()); + [this, initialPose = exampleTrajectory.InitialPose()] { + m_drive.ResetOdometry(initialPose); }, {}) .AndThen(std::move(ramseteCommand)) diff --git a/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/subsystems/DriveSubsystem.cpp b/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/subsystems/DriveSubsystem.cpp index 0bc598e4e9d..d367607aecc 100644 --- a/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/subsystems/DriveSubsystem.cpp +++ b/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/subsystems/DriveSubsystem.cpp @@ -17,10 +17,16 @@ DriveSubsystem::DriveSubsystem() m_leftEncoder{kLeftEncoderPorts[0], kLeftEncoderPorts[1]}, m_rightEncoder{kRightEncoderPorts[0], kRightEncoderPorts[1]}, m_odometry{m_gyro.GetRotation2d(), units::meter_t{0}, units::meter_t{0}} { + wpi::SendableRegistry::AddChild(&m_drive, &m_left1); + wpi::SendableRegistry::AddChild(&m_drive, &m_right1); + + m_left1.AddFollower(m_left2); + m_right1.AddFollower(m_right2); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.SetInverted(true); + m_right1.SetInverted(true); // Set the distance per pulse for the encoders m_leftEncoder.SetDistancePerPulse(kEncoderDistancePerPulse.value()); @@ -41,8 +47,8 @@ void DriveSubsystem::ArcadeDrive(double fwd, double rot) { } void DriveSubsystem::TankDriveVolts(units::volt_t left, units::volt_t right) { - m_leftMotors.SetVoltage(left); - m_rightMotors.SetVoltage(right); + m_left1.SetVoltage(left); + m_right1.SetVoltage(right); m_drive.Feed(); } @@ -85,7 +91,6 @@ frc::DifferentialDriveWheelSpeeds DriveSubsystem::GetWheelSpeeds() { } void DriveSubsystem::ResetOdometry(frc::Pose2d pose) { - ResetEncoders(); m_odometry.ResetPosition(m_gyro.GetRotation2d(), units::meter_t{m_leftEncoder.GetDistance()}, units::meter_t{m_rightEncoder.GetDistance()}, pose); diff --git a/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/subsystems/DriveSubsystem.h index 8ea14dad398..e1580e1b480 100644 --- a/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/subsystems/DriveSubsystem.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -122,14 +121,9 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right1; frc::PWMSparkMax m_right2; - // The motors on the left side of the drive - frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; - - // The motors on the right side of the drive - frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; - // The robot's drive - frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; + frc::DifferentialDrive m_drive{[&](double output) { m_left1.Set(output); }, + [&](double output) { m_right1.Set(output); }}; // The left-side drive encoder frc::Encoder m_leftEncoder; diff --git a/wpilibcExamples/src/main/cpp/examples/RamseteController/cpp/Drivetrain.cpp b/wpilibcExamples/src/main/cpp/examples/RamseteController/cpp/Drivetrain.cpp index 8c1e6323bed..9ce08e9b9ad 100644 --- a/wpilibcExamples/src/main/cpp/examples/RamseteController/cpp/Drivetrain.cpp +++ b/wpilibcExamples/src/main/cpp/examples/RamseteController/cpp/Drivetrain.cpp @@ -12,8 +12,8 @@ void Drivetrain::SetSpeeds(const frc::DifferentialDriveWheelSpeeds& speeds) { const double rightOutput = m_rightPIDController.Calculate( m_rightEncoder.GetRate(), speeds.right.value()); - m_leftGroup.SetVoltage(units::volt_t{leftOutput} + leftFeedforward); - m_rightGroup.SetVoltage(units::volt_t{rightOutput} + rightFeedforward); + m_leftLeader.SetVoltage(units::volt_t{leftOutput} + leftFeedforward); + m_rightLeader.SetVoltage(units::volt_t{rightOutput} + rightFeedforward); } void Drivetrain::Drive(units::meters_per_second_t xSpeed, diff --git a/wpilibcExamples/src/main/cpp/examples/RamseteController/include/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/RamseteController/include/Drivetrain.h index 341cd38c1cd..39511c7a4b7 100644 --- a/wpilibcExamples/src/main/cpp/examples/RamseteController/include/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/RamseteController/include/Drivetrain.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -25,10 +24,13 @@ class Drivetrain { Drivetrain() { m_gyro.Reset(); + m_leftLeader.AddFollower(m_leftFollower); + m_rightLeader.AddFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightGroup.SetInverted(true); + m_rightLeader.SetInverted(true); // Set the distance per pulse for the drive encoders. We can simply use the // distance traveled for one rotation of the wheel divided by the encoder @@ -64,9 +66,6 @@ class Drivetrain { frc::PWMSparkMax m_rightLeader{3}; frc::PWMSparkMax m_rightFollower{4}; - frc::MotorControllerGroup m_leftGroup{m_leftLeader, m_leftFollower}; - frc::MotorControllerGroup m_rightGroup{m_rightLeader, m_rightFollower}; - frc::Encoder m_leftEncoder{0, 1}; frc::Encoder m_rightEncoder{2, 3}; diff --git a/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/RapidReactCommandBot.cpp b/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/RapidReactCommandBot.cpp index 1b46affa21a..653aad3ce52 100644 --- a/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/RapidReactCommandBot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/RapidReactCommandBot.cpp @@ -12,16 +12,13 @@ void RapidReactCommandBot::ConfigureBindings() { // Automatically run the storage motor whenever the ball storage is not full, - // and turn it off whenever it fills. - frc2::Trigger([this] { - return m_storage.IsFull(); - }).WhileFalse(m_storage.RunCommand()); + // and turn it off whenever it fills. Uses subsystem-hosted trigger to + // improve readability and make inter-subsystem communication easier. + m_storage.HasCargo.WhileFalse(m_storage.RunCommand()); // Automatically disable and retract the intake whenever the ball storage is // full. - frc2::Trigger([this] { - return m_storage.IsFull(); - }).OnTrue(m_intake.RetractCommand()); + m_storage.HasCargo.OnTrue(m_intake.RetractCommand()); // Control the drive with split-stick arcade controls m_drive.SetDefaultCommand(m_drive.ArcadeDriveCommand( diff --git a/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/subsystems/Drive.cpp b/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/subsystems/Drive.cpp index 98ff0cac75f..e03ce432124 100644 --- a/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/subsystems/Drive.cpp +++ b/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/subsystems/Drive.cpp @@ -9,10 +9,16 @@ #include Drive::Drive() { + wpi::SendableRegistry::AddChild(&m_drive, &m_leftLeader); + wpi::SendableRegistry::AddChild(&m_drive, &m_rightLeader); + + m_leftLeader.AddFollower(m_leftFollower); + m_rightLeader.AddFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.SetInverted(true); + m_rightLeader.SetInverted(true); // Sets the distance per pulse for the encoders m_leftEncoder.SetDistancePerPulse(DriveConstants::kEncoderDistancePerPulse); diff --git a/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/subsystems/Storage.cpp b/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/subsystems/Storage.cpp index 3e9dc6203be..59c2a023516 100644 --- a/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/subsystems/Storage.cpp +++ b/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/subsystems/Storage.cpp @@ -12,7 +12,3 @@ Storage::Storage() { frc2::CommandPtr Storage::RunCommand() { return Run([this] { m_motor.Set(1.0); }).WithName("Run"); } - -bool Storage::IsFull() const { - return m_ballSensor.Get(); -} diff --git a/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Drive.h b/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Drive.h index ac96c52839c..8432e57b6b0 100644 --- a/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Drive.h +++ b/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Drive.h @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -45,10 +44,9 @@ class Drive : public frc2::SubsystemBase { frc::PWMSparkMax m_rightLeader{DriveConstants::kRightMotor1Port}; frc::PWMSparkMax m_rightFollower{DriveConstants::kRightMotor2Port}; - frc::MotorControllerGroup m_leftMotors{m_leftLeader, m_leftFollower}; - frc::MotorControllerGroup m_rightMotors{m_rightLeader, m_rightFollower}; - - frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; + frc::DifferentialDrive m_drive{ + [&](double output) { m_leftLeader.Set(output); }, + [&](double output) { m_rightLeader.Set(output); }}; frc::Encoder m_leftEncoder{DriveConstants::kLeftEncoderPorts[0], DriveConstants::kLeftEncoderPorts[1], diff --git a/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Storage.h b/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Storage.h index 58694b3035d..ec99118d600 100644 --- a/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Storage.h +++ b/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Storage.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "Constants.h" @@ -19,7 +20,7 @@ class Storage : frc2::SubsystemBase { frc2::CommandPtr RunCommand(); /** Whether the ball storage is full. */ - bool IsFull() const; + frc2::Trigger HasCargo{[this] { return m_ballSensor.Get(); }}; private: frc::PWMSparkMax m_motor{StorageConstants::kMotorPort}; diff --git a/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/subsystems/Drivetrain.cpp b/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/subsystems/Drivetrain.cpp index 979f8a0e5d4..1d212d5c5d8 100644 --- a/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/subsystems/Drivetrain.cpp +++ b/wpilibcExamples/src/main/cpp/examples/RomiReference/cpp/subsystems/Drivetrain.cpp @@ -15,6 +15,9 @@ using namespace DriveConstants; // The Romi has onboard encoders that are hardcoded // to use DIO pins 4/5 and 6/7 for the left and right Drivetrain::Drivetrain() { + wpi::SendableRegistry::AddChild(&m_drive, &m_leftMotor); + wpi::SendableRegistry::AddChild(&m_drive, &m_rightMotor); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. diff --git a/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/Drivetrain.h index ace7d33bc77..d679178fd04 100644 --- a/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/Drivetrain.h @@ -114,7 +114,9 @@ class Drivetrain : public frc2::SubsystemBase { frc::Encoder m_leftEncoder{4, 5}; frc::Encoder m_rightEncoder{6, 7}; - frc::DifferentialDrive m_drive{m_leftMotor, m_rightMotor}; + frc::DifferentialDrive m_drive{ + [&](double output) { m_leftMotor.Set(output); }, + [&](double output) { m_rightMotor.Set(output); }}; frc::RomiGyro m_gyro; frc::BuiltInAccelerometer m_accelerometer; diff --git a/wpilibcExamples/src/main/cpp/examples/ShuffleBoard/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/ShuffleBoard/cpp/Robot.cpp index b951f18bbe9..b532bcbf75b 100644 --- a/wpilibcExamples/src/main/cpp/examples/ShuffleBoard/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/ShuffleBoard/cpp/Robot.cpp @@ -30,6 +30,9 @@ class Robot : public frc::TimedRobot { public: void RobotInit() override { + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_left); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_right); + // Add a widget titled 'Max Speed' with a number slider. m_maxSpeed = frc::Shuffleboard::GetTab("Configuration") .Add("Max Speed", 1) @@ -65,7 +68,9 @@ class Robot : public frc::TimedRobot { frc::PWMSparkMax m_right{1}; frc::PWMSparkMax m_elevatorMotor{2}; - frc::DifferentialDrive m_robotDrive{m_left, m_right}; + frc::DifferentialDrive m_robotDrive{ + [&](double output) { m_left.Set(output); }, + [&](double output) { m_right.Set(output); }}; frc::Joystick m_stick{0}; diff --git a/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/cpp/Drivetrain.cpp b/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/cpp/Drivetrain.cpp index 93d0889df50..603facaa2f0 100644 --- a/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/cpp/Drivetrain.cpp +++ b/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/cpp/Drivetrain.cpp @@ -14,8 +14,8 @@ void Drivetrain::SetSpeeds(const frc::DifferentialDriveWheelSpeeds& speeds) { double rightOutput = m_rightPIDController.Calculate(m_rightEncoder.GetRate(), speeds.right.value()); - m_leftGroup.SetVoltage(units::volt_t{leftOutput} + leftFeedforward); - m_rightGroup.SetVoltage(units::volt_t{rightOutput} + rightFeedforward); + m_leftLeader.SetVoltage(units::volt_t{leftOutput} + leftFeedforward); + m_rightLeader.SetVoltage(units::volt_t{rightOutput} + rightFeedforward); } void Drivetrain::Drive(units::meters_per_second_t xSpeed, @@ -30,8 +30,6 @@ void Drivetrain::UpdateOdometry() { } void Drivetrain::ResetOdometry(const frc::Pose2d& pose) { - m_leftEncoder.Reset(); - m_rightEncoder.Reset(); m_drivetrainSimulator.SetPose(pose); m_odometry.ResetPosition(m_gyro.GetRotation2d(), units::meter_t{m_leftEncoder.GetDistance()}, @@ -43,9 +41,9 @@ void Drivetrain::SimulationPeriodic() { // simulation, and write the simulated positions and velocities to our // simulated encoder and gyro. We negate the right side so that positive // voltages make the right side move forward. - m_drivetrainSimulator.SetInputs(units::volt_t{m_leftGroup.Get()} * + m_drivetrainSimulator.SetInputs(units::volt_t{m_leftLeader.Get()} * frc::RobotController::GetInputVoltage(), - units::volt_t{m_rightGroup.Get()} * + units::volt_t{m_rightLeader.Get()} * frc::RobotController::GetInputVoltage()); m_drivetrainSimulator.Update(20_ms); diff --git a/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/include/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/include/Drivetrain.h index 4c274fe476d..ca82547f7cb 100644 --- a/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/include/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/include/Drivetrain.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -33,10 +32,13 @@ class Drivetrain { Drivetrain() { m_gyro.Reset(); + m_leftLeader.AddFollower(m_leftFollower); + m_rightLeader.AddFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightGroup.SetInverted(true); + m_rightLeader.SetInverted(true); // Set the distance per pulse for the drive encoders. We can simply use the // distance traveled for one rotation of the wheel divided by the encoder @@ -49,7 +51,7 @@ class Drivetrain { m_leftEncoder.Reset(); m_rightEncoder.Reset(); - m_rightGroup.SetInverted(true); + m_rightLeader.SetInverted(true); frc::SmartDashboard::PutData("Field", &m_fieldSim); } @@ -80,9 +82,6 @@ class Drivetrain { frc::PWMSparkMax m_rightLeader{3}; frc::PWMSparkMax m_rightFollower{4}; - frc::MotorControllerGroup m_leftGroup{m_leftLeader, m_leftFollower}; - frc::MotorControllerGroup m_rightGroup{m_rightLeader, m_rightFollower}; - frc::Encoder m_leftEncoder{0, 1}; frc::Encoder m_rightEncoder{2, 3}; diff --git a/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/cpp/subsystems/DriveSubsystem.cpp b/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/cpp/subsystems/DriveSubsystem.cpp index 15d85964359..721481db297 100644 --- a/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/cpp/subsystems/DriveSubsystem.cpp +++ b/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/cpp/subsystems/DriveSubsystem.cpp @@ -14,10 +14,16 @@ using namespace DriveConstants; DriveSubsystem::DriveSubsystem() { + wpi::SendableRegistry::AddChild(&m_drive, &m_left1); + wpi::SendableRegistry::AddChild(&m_drive, &m_right1); + + m_left1.AddFollower(m_left2); + m_right1.AddFollower(m_right2); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.SetInverted(true); + m_right1.SetInverted(true); // Set the distance per pulse for the encoders m_leftEncoder.SetDistancePerPulse(kEncoderDistancePerPulse); @@ -40,10 +46,9 @@ void DriveSubsystem::SimulationPeriodic() { // simulation, and write the simulated positions and velocities to our // simulated encoder and gyro. We negate the right side so that positive // voltages make the right side move forward. - m_drivetrainSimulator.SetInputs(units::volt_t{m_leftMotors.Get()} * - frc::RobotController::GetInputVoltage(), - units::volt_t{m_rightMotors.Get()} * - frc::RobotController::GetInputVoltage()); + m_drivetrainSimulator.SetInputs( + units::volt_t{m_left1.Get()} * frc::RobotController::GetInputVoltage(), + units::volt_t{m_right1.Get()} * frc::RobotController::GetInputVoltage()); m_drivetrainSimulator.Update(20_ms); m_leftEncoderSim.SetDistance(m_drivetrainSimulator.GetLeftPosition().value()); @@ -63,8 +68,8 @@ void DriveSubsystem::ArcadeDrive(double fwd, double rot) { } void DriveSubsystem::TankDriveVolts(units::volt_t left, units::volt_t right) { - m_leftMotors.SetVoltage(left); - m_rightMotors.SetVoltage(right); + m_left1.SetVoltage(left); + m_right1.SetVoltage(right); m_drive.Feed(); } @@ -107,7 +112,6 @@ frc::DifferentialDriveWheelSpeeds DriveSubsystem::GetWheelSpeeds() { } void DriveSubsystem::ResetOdometry(frc::Pose2d pose) { - ResetEncoders(); m_drivetrainSimulator.SetPose(pose); m_odometry.ResetPosition(m_gyro.GetRotation2d(), units::meter_t{m_leftEncoder.GetDistance()}, diff --git a/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/include/subsystems/DriveSubsystem.h index 57392c723ba..5e412019bca 100644 --- a/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/include/subsystems/DriveSubsystem.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -133,14 +132,9 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right1{DriveConstants::kRightMotor1Port}; frc::PWMSparkMax m_right2{DriveConstants::kRightMotor2Port}; - // The motors on the left side of the drive - frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; - - // The motors on the right side of the drive - frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; - // The robot's drive - frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; + frc::DifferentialDrive m_drive{[&](double output) { m_left1.Set(output); }, + [&](double output) { m_right1.Set(output); }}; // The left-side drive encoder frc::Encoder m_leftEncoder{DriveConstants::kLeftEncoderPorts[0], diff --git a/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/RobotContainer.cpp b/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/RobotContainer.cpp index 2ca4b718933..6c8de057532 100644 --- a/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/RobotContainer.cpp +++ b/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/cpp/RobotContainer.cpp @@ -93,8 +93,8 @@ frc2::CommandPtr RobotContainer::GetAutonomousCommand() { // command, then stop at the end. return frc2::cmd::Sequence( frc2::InstantCommand( - [this, &exampleTrajectory]() { - m_drive.ResetOdometry(exampleTrajectory.InitialPose()); + [this, initialPose = exampleTrajectory.InitialPose()]() { + m_drive.ResetOdometry(initialPose); }, {}) .ToPtr(), diff --git a/wpilibcExamples/src/main/cpp/examples/SysId/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/SysId/cpp/Robot.cpp new file mode 100644 index 00000000000..32782b29ae2 --- /dev/null +++ b/wpilibcExamples/src/main/cpp/examples/SysId/cpp/Robot.cpp @@ -0,0 +1,55 @@ +// 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 "Robot.h" + +#include + +void Robot::RobotInit() {} + +void Robot::RobotPeriodic() { + frc2::CommandScheduler::GetInstance().Run(); +} + +void Robot::DisabledInit() {} + +void Robot::DisabledPeriodic() {} + +void Robot::DisabledExit() {} + +void Robot::AutonomousInit() { + m_autonomousCommand = m_container.GetAutonomousCommand(); + + if (m_autonomousCommand) { + m_autonomousCommand->Schedule(); + } +} + +void Robot::AutonomousPeriodic() {} + +void Robot::AutonomousExit() {} + +void Robot::TeleopInit() { + if (m_autonomousCommand) { + m_autonomousCommand->Cancel(); + } +} + +void Robot::TeleopPeriodic() {} + +void Robot::TeleopExit() {} + +void Robot::TestInit() { + frc2::CommandScheduler::GetInstance().CancelAll(); +} + +void Robot::TestPeriodic() {} + +void Robot::TestExit() {} + +#ifndef RUNNING_FRC_TESTS +int main() { + return frc::StartRobot(); +} +#endif diff --git a/wpilibcExamples/src/main/cpp/examples/SysId/cpp/SysIdRoutineBot.cpp b/wpilibcExamples/src/main/cpp/examples/SysId/cpp/SysIdRoutineBot.cpp new file mode 100644 index 00000000000..45d480fe0b6 --- /dev/null +++ b/wpilibcExamples/src/main/cpp/examples/SysId/cpp/SysIdRoutineBot.cpp @@ -0,0 +1,30 @@ +// 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 "SysIdRoutineBot.h" + +#include + +SysIdRoutineBot::SysIdRoutineBot() { + ConfigureBindings(); +} + +void SysIdRoutineBot::ConfigureBindings() { + m_drive.SetDefaultCommand(m_drive.ArcadeDriveCommand( + [this] { return -m_driverController.GetLeftY(); }, + [this] { return -m_driverController.GetRightX(); })); + + m_driverController.A().WhileTrue( + m_drive.SysIdQuasistatic(frc2::sysid::Direction::kForward)); + m_driverController.B().WhileTrue( + m_drive.SysIdQuasistatic(frc2::sysid::Direction::kReverse)); + m_driverController.X().WhileTrue( + m_drive.SysIdDynamic(frc2::sysid::Direction::kForward)); + m_driverController.Y().WhileTrue( + m_drive.SysIdDynamic(frc2::sysid::Direction::kReverse)); +} + +frc2::CommandPtr SysIdRoutineBot::GetAutonomousCommand() { + return m_drive.Run([] {}); +} diff --git a/wpilibcExamples/src/main/cpp/examples/SysId/cpp/subsystems/Drive.cpp b/wpilibcExamples/src/main/cpp/examples/SysId/cpp/subsystems/Drive.cpp new file mode 100644 index 00000000000..331c17afc40 --- /dev/null +++ b/wpilibcExamples/src/main/cpp/examples/SysId/cpp/subsystems/Drive.cpp @@ -0,0 +1,37 @@ +// 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 "subsystems/Drive.h" + +#include + +Drive::Drive() { + m_leftMotor.AddFollower(frc::PWMSparkMax{constants::drive::kLeftMotor2Port}); + m_rightMotor.AddFollower( + frc::PWMSparkMax{constants::drive::kRightMotor2Port}); + + m_rightMotor.SetInverted(true); + + m_leftEncoder.SetDistancePerPulse( + constants::drive::kEncoderDistancePerPulse.value()); + m_rightEncoder.SetDistancePerPulse( + constants::drive::kEncoderDistancePerPulse.value()); + + m_drive.SetSafetyEnabled(false); +} + +frc2::CommandPtr Drive::ArcadeDriveCommand(std::function fwd, + std::function rot) { + return frc2::cmd::Run([this, fwd, rot] { m_drive.ArcadeDrive(fwd(), rot()); }, + {this}) + .WithName("Arcade Drive"); +} + +frc2::CommandPtr Drive::SysIdQuasistatic(frc2::sysid::Direction direction) { + return m_sysIdRoutine.Quasistatic(direction); +} + +frc2::CommandPtr Drive::SysIdDynamic(frc2::sysid::Direction direction) { + return m_sysIdRoutine.Dynamic(direction); +} diff --git a/wpilibcExamples/src/main/cpp/examples/SysId/include/Constants.h b/wpilibcExamples/src/main/cpp/examples/SysId/include/Constants.h new file mode 100644 index 00000000000..e01f533afee --- /dev/null +++ b/wpilibcExamples/src/main/cpp/examples/SysId/include/Constants.h @@ -0,0 +1,81 @@ +// 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 +#include + +#include +#include +#include +#include +#include + +namespace constants { +namespace drive { +inline constexpr int kLeftMotor1Port = 0; +inline constexpr int kLeftMotor2Port = 1; +inline constexpr int kRightMotor1Port = 2; +inline constexpr int kRightMotor2Port = 3; + +inline constexpr std::array kLeftEncoderPorts = {0, 1}; +inline constexpr std::array kRightEncoderPorts = {2, 3}; +inline constexpr bool kLeftEncoderReversed = false; +inline constexpr bool kRightEncoderReversed = true; + +inline constexpr int kEncoderCpr = 1024; +inline constexpr units::meter_t kWheelDiameter = 6_in; +inline constexpr units::meter_t kEncoderDistancePerPulse = + (kWheelDiameter * std::numbers::pi) / static_cast(kEncoderCpr); +} // namespace drive + +namespace shooter { + +using kv_unit = + units::compound_unit, + units::inverse>; +using kv_unit_t = units::unit_t; + +inline constexpr std::array kEncoderPorts = {4, 5}; +inline constexpr bool kLeftEncoderReversed = false; +inline constexpr int kEncoderCpr = 1024; +inline constexpr units::turn_t kEncoderDistancePerPulse = + units::turn_t{1.0} / static_cast(kEncoderCpr); + +inline constexpr int kShooterMotorPort = 4; +inline constexpr int kFeederMotorPort = 5; + +inline constexpr units::turns_per_second_t kShooterFreeSpeed = 5300_tps; +inline constexpr units::turns_per_second_t kShooterTargetSpeed = 4000_tps; +inline constexpr units::turns_per_second_t kShooterTolerance = 50_tps; + +inline constexpr double kP = 1.0; + +inline constexpr units::volt_t kS = 0.05_V; +inline constexpr kv_unit_t kV = (12_V) / kShooterFreeSpeed; + +inline constexpr double kFeederSpeed = 0.5; +} // namespace shooter + +namespace intake { +inline constexpr int kMotorPort = 6; +inline constexpr std::array kSolenoidPorts = {2, 3}; +} // namespace intake + +namespace storage { +inline constexpr int kMotorPort = 7; +inline constexpr int kBallSensorPort = 6; +} // namespace storage + +namespace autonomous { +inline constexpr units::second_t kTimeout = 3_s; +inline constexpr units::meter_t kDriveDistance = 2_m; +inline constexpr double kDriveSpeed = 0.5; +} // namespace autonomous + +namespace oi { +inline constexpr int kDriverControllerPort = 0; +} // namespace oi +} // namespace constants diff --git a/wpilibcExamples/src/main/cpp/examples/SysId/include/Robot.h b/wpilibcExamples/src/main/cpp/examples/SysId/include/Robot.h new file mode 100644 index 00000000000..b6142c2405b --- /dev/null +++ b/wpilibcExamples/src/main/cpp/examples/SysId/include/Robot.h @@ -0,0 +1,35 @@ +// 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 + +#include +#include + +#include "SysIdRoutineBot.h" + +class Robot : public frc::TimedRobot { + public: + void RobotInit() override; + void RobotPeriodic() override; + void DisabledInit() override; + void DisabledPeriodic() override; + void DisabledExit() override; + void AutonomousInit() override; + void AutonomousPeriodic() override; + void AutonomousExit() override; + void TeleopInit() override; + void TeleopPeriodic() override; + void TeleopExit() override; + void TestInit() override; + void TestPeriodic() override; + void TestExit() override; + + private: + std::optional m_autonomousCommand; + + SysIdRoutineBot m_container; +}; diff --git a/wpilibcExamples/src/main/cpp/examples/SysId/include/SysIdRoutineBot.h b/wpilibcExamples/src/main/cpp/examples/SysId/include/SysIdRoutineBot.h new file mode 100644 index 00000000000..31110fc57a6 --- /dev/null +++ b/wpilibcExamples/src/main/cpp/examples/SysId/include/SysIdRoutineBot.h @@ -0,0 +1,24 @@ +// 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 +#include + +#include "Constants.h" +#include "subsystems/Drive.h" + +class SysIdRoutineBot { + public: + SysIdRoutineBot(); + + frc2::CommandPtr GetAutonomousCommand(); + + private: + void ConfigureBindings(); + frc2::CommandXboxController m_driverController{ + constants::oi::kDriverControllerPort}; + Drive m_drive; +}; diff --git a/wpilibcExamples/src/main/cpp/examples/SysId/include/subsystems/Drive.h b/wpilibcExamples/src/main/cpp/examples/SysId/include/subsystems/Drive.h new file mode 100644 index 00000000000..61d34f131f7 --- /dev/null +++ b/wpilibcExamples/src/main/cpp/examples/SysId/include/subsystems/Drive.h @@ -0,0 +1,62 @@ +// 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 + +#include +#include +#include +#include +#include +#include + +#include "Constants.h" + +class Drive : public frc2::SubsystemBase { + public: + Drive(); + + frc2::CommandPtr ArcadeDriveCommand(std::function fwd, + std::function rot); + frc2::CommandPtr SysIdQuasistatic(frc2::sysid::Direction direction); + frc2::CommandPtr SysIdDynamic(frc2::sysid::Direction direction); + + private: + frc::PWMSparkMax m_leftMotor{constants::drive::kLeftMotor1Port}; + frc::PWMSparkMax m_rightMotor{constants::drive::kRightMotor1Port}; + frc::DifferentialDrive m_drive{[this](auto val) { m_leftMotor.Set(val); }, + [this](auto val) { m_rightMotor.Set(val); }}; + + frc::Encoder m_leftEncoder{constants::drive::kLeftEncoderPorts[0], + constants::drive::kLeftEncoderPorts[1], + constants::drive::kLeftEncoderReversed}; + + frc::Encoder m_rightEncoder{constants::drive::kRightEncoderPorts[0], + constants::drive::kRightEncoderPorts[1], + constants::drive::kRightEncoderReversed}; + + frc2::sysid::SysIdRoutine m_sysIdRoutine{ + frc2::sysid::Config{std::nullopt, std::nullopt, std::nullopt, + std::nullopt}, + frc2::sysid::Mechanism{ + [this](units::volt_t driveVoltage) { + m_leftMotor.SetVoltage(driveVoltage); + m_rightMotor.SetVoltage(driveVoltage); + }, + [this](frc::sysid::SysIdRoutineLog* log) { + log->Motor("drive-left") + .voltage(m_leftMotor.Get() * + frc::RobotController::GetBatteryVoltage()) + .position(units::meter_t{m_leftEncoder.GetDistance()}) + .velocity(units::meters_per_second_t{m_leftEncoder.GetRate()}); + log->Motor("drive-right") + .voltage(m_rightMotor.Get() * + frc::RobotController::GetBatteryVoltage()) + .position(units::meter_t{m_rightEncoder.GetDistance()}) + .velocity(units::meters_per_second_t{m_rightEncoder.GetRate()}); + }, + this}}; +}; diff --git a/wpilibcExamples/src/main/cpp/examples/TankDrive/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/TankDrive/cpp/Robot.cpp index 4e48d475842..f7b3e9c6900 100644 --- a/wpilibcExamples/src/main/cpp/examples/TankDrive/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/TankDrive/cpp/Robot.cpp @@ -14,12 +14,17 @@ class Robot : public frc::TimedRobot { frc::PWMSparkMax m_leftMotor{0}; frc::PWMSparkMax m_rightMotor{1}; - frc::DifferentialDrive m_robotDrive{m_leftMotor, m_rightMotor}; + frc::DifferentialDrive m_robotDrive{ + [&](double output) { m_leftMotor.Set(output); }, + [&](double output) { m_rightMotor.Set(output); }}; frc::Joystick m_leftStick{0}; frc::Joystick m_rightStick{1}; public: void RobotInit() override { + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_leftMotor); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_rightMotor); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. diff --git a/wpilibcExamples/src/main/cpp/examples/TankDriveXboxController/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/TankDriveXboxController/cpp/Robot.cpp index b8cff3271e7..7c0706ef791 100644 --- a/wpilibcExamples/src/main/cpp/examples/TankDriveXboxController/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/TankDriveXboxController/cpp/Robot.cpp @@ -14,11 +14,16 @@ class Robot : public frc::TimedRobot { frc::PWMSparkMax m_leftMotor{0}; frc::PWMSparkMax m_rightMotor{1}; - frc::DifferentialDrive m_robotDrive{m_leftMotor, m_rightMotor}; + frc::DifferentialDrive m_robotDrive{ + [&](double output) { m_leftMotor.Set(output); }, + [&](double output) { m_rightMotor.Set(output); }}; frc::XboxController m_driverController{0}; public: void RobotInit() override { + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_leftMotor); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_rightMotor); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. diff --git a/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/cpp/Robot.cpp index 9d2b5c2003c..fecaf97fb9d 100644 --- a/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/cpp/Robot.cpp @@ -4,6 +4,11 @@ #include "Robot.h" +Robot::Robot() { + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_left); + wpi::SendableRegistry::AddChild(&m_robotDrive, &m_right); +} + void Robot::AutonomousInit() { // Set setpoint of the pid controller m_pidController.SetSetpoint(kHoldDistance.value()); diff --git a/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/include/Robot.h b/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/include/Robot.h index 4d225764f01..696f0668fc9 100644 --- a/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/include/Robot.h +++ b/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/include/Robot.h @@ -18,6 +18,7 @@ */ class Robot : public frc::TimedRobot { public: + Robot(); void AutonomousInit() override; void AutonomousPeriodic() override; @@ -44,6 +45,8 @@ class Robot : public frc::TimedRobot { frc::Ultrasonic m_ultrasonic{kUltrasonicPingPort, kUltrasonicEchoPort}; frc::PWMSparkMax m_left{kLeftMotorPort}; frc::PWMSparkMax m_right{kRightMotorPort}; - frc::DifferentialDrive m_robotDrive{m_left, m_right}; + frc::DifferentialDrive m_robotDrive{ + [&](double output) { m_left.Set(output); }, + [&](double output) { m_right.Set(output); }}; frc::PIDController m_pidController{kP, kI, kD}; }; diff --git a/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/subsystems/Drivetrain.cpp b/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/subsystems/Drivetrain.cpp index bcec018bcda..25c520cef25 100644 --- a/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/subsystems/Drivetrain.cpp +++ b/wpilibcExamples/src/main/cpp/examples/XRPReference/cpp/subsystems/Drivetrain.cpp @@ -15,6 +15,9 @@ using namespace DriveConstants; // The XRP has onboard encoders that are hardcoded // to use DIO pins 4/5 and 6/7 for the left and right Drivetrain::Drivetrain() { + wpi::SendableRegistry::AddChild(&m_drive, &m_leftMotor); + wpi::SendableRegistry::AddChild(&m_drive, &m_rightMotor); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. diff --git a/wpilibcExamples/src/main/cpp/examples/XRPReference/include/subsystems/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/XRPReference/include/subsystems/Drivetrain.h index e665601228e..c2a2ef319f1 100644 --- a/wpilibcExamples/src/main/cpp/examples/XRPReference/include/subsystems/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/XRPReference/include/subsystems/Drivetrain.h @@ -118,7 +118,9 @@ class Drivetrain : public frc2::SubsystemBase { frc::Encoder m_leftEncoder{4, 5}; frc::Encoder m_rightEncoder{6, 7}; - frc::DifferentialDrive m_drive{m_leftMotor, m_rightMotor}; + frc::DifferentialDrive m_drive{ + [&](double output) { m_leftMotor.Set(output); }, + [&](double output) { m_rightMotor.Set(output); }}; frc::XRPGyro m_gyro; frc::BuiltInAccelerometer m_accelerometer; diff --git a/wpilibcExamples/src/main/cpp/examples/examples.json b/wpilibcExamples/src/main/cpp/examples/examples.json index 803290918cc..486e2ec62b1 100644 --- a/wpilibcExamples/src/main/cpp/examples/examples.json +++ b/wpilibcExamples/src/main/cpp/examples/examples.json @@ -927,5 +927,17 @@ "foldername": "FlywheelBangBangController", "gradlebase": "cpp", "commandversion": 2 + }, + { + "name": "SysIdRoutine", + "description": "A sample command-based robot demonstrating use of the SysIdRoutine command factory", + "tags": [ + "SysId", + "Command-based", + "DataLog" + ], + "foldername": "SysId", + "gradlebase": "cpp", + "commandversion": 2 } ] diff --git a/wpilibcExamples/src/test/cpp/examples/MecanumControllerCommand/cpp/MecanumControllerCommandTest.cpp b/wpilibcExamples/src/test/cpp/examples/MecanumControllerCommand/cpp/MecanumControllerCommandTest.cpp new file mode 100644 index 00000000000..74d84f167fa --- /dev/null +++ b/wpilibcExamples/src/test/cpp/examples/MecanumControllerCommand/cpp/MecanumControllerCommandTest.cpp @@ -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. + +#include + +#include +#include +#include +#include + +#include "Robot.h" + +class MecanumControllerCommandTest : public testing::Test { + Robot m_robot; + std::optional m_thread; + bool joystickWarning; + + public: + void SetUp() override { + frc::sim::PauseTiming(); + joystickWarning = frc::DriverStation::IsJoystickConnectionWarningSilenced(); + frc::DriverStation::SilenceJoystickConnectionWarning(true); + + m_thread = std::thread([&] { m_robot.StartCompetition(); }); + frc::sim::StepTiming(0.0_ms); // Wait for Notifiers + } + + void TearDown() override { + m_robot.EndCompetition(); + m_thread->join(); + + frc::sim::DriverStationSim::ResetData(); + frc::DriverStation::SilenceJoystickConnectionWarning(joystickWarning); + } +}; + +TEST_F(MecanumControllerCommandTest, Match) { + // auto + frc::sim::DriverStationSim::SetAutonomous(true); + frc::sim::DriverStationSim::SetEnabled(true); + frc::sim::DriverStationSim::NotifyNewData(); + + frc::sim::StepTiming(15_s); + + // brief disabled period- exact duration shouldn't matter + frc::sim::DriverStationSim::SetAutonomous(false); + frc::sim::DriverStationSim::SetEnabled(false); + frc::sim::DriverStationSim::NotifyNewData(); + + frc::sim::StepTiming(3_s); + + // teleop + frc::sim::DriverStationSim::SetAutonomous(false); + frc::sim::DriverStationSim::SetEnabled(true); + frc::sim::DriverStationSim::NotifyNewData(); + + frc::sim::StepTiming(135_s); + + // end of match + frc::sim::DriverStationSim::SetAutonomous(false); + frc::sim::DriverStationSim::SetEnabled(false); + frc::sim::DriverStationSim::NotifyNewData(); +} diff --git a/wpilibcExamples/src/test/cpp/examples/MecanumControllerCommand/cpp/main.cpp b/wpilibcExamples/src/test/cpp/examples/MecanumControllerCommand/cpp/main.cpp new file mode 100644 index 00000000000..38d5cfa2fed --- /dev/null +++ b/wpilibcExamples/src/test/cpp/examples/MecanumControllerCommand/cpp/main.cpp @@ -0,0 +1,16 @@ +// 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 +#include + +/** + * Runs all unit tests. + */ +int main(int argc, char** argv) { + HAL_Initialize(500, 0); + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/wpilibcExamples/src/test/cpp/examples/RamseteCommand/cpp/RamseteCommandTest.cpp b/wpilibcExamples/src/test/cpp/examples/RamseteCommand/cpp/RamseteCommandTest.cpp new file mode 100644 index 00000000000..74d84f167fa --- /dev/null +++ b/wpilibcExamples/src/test/cpp/examples/RamseteCommand/cpp/RamseteCommandTest.cpp @@ -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. + +#include + +#include +#include +#include +#include + +#include "Robot.h" + +class MecanumControllerCommandTest : public testing::Test { + Robot m_robot; + std::optional m_thread; + bool joystickWarning; + + public: + void SetUp() override { + frc::sim::PauseTiming(); + joystickWarning = frc::DriverStation::IsJoystickConnectionWarningSilenced(); + frc::DriverStation::SilenceJoystickConnectionWarning(true); + + m_thread = std::thread([&] { m_robot.StartCompetition(); }); + frc::sim::StepTiming(0.0_ms); // Wait for Notifiers + } + + void TearDown() override { + m_robot.EndCompetition(); + m_thread->join(); + + frc::sim::DriverStationSim::ResetData(); + frc::DriverStation::SilenceJoystickConnectionWarning(joystickWarning); + } +}; + +TEST_F(MecanumControllerCommandTest, Match) { + // auto + frc::sim::DriverStationSim::SetAutonomous(true); + frc::sim::DriverStationSim::SetEnabled(true); + frc::sim::DriverStationSim::NotifyNewData(); + + frc::sim::StepTiming(15_s); + + // brief disabled period- exact duration shouldn't matter + frc::sim::DriverStationSim::SetAutonomous(false); + frc::sim::DriverStationSim::SetEnabled(false); + frc::sim::DriverStationSim::NotifyNewData(); + + frc::sim::StepTiming(3_s); + + // teleop + frc::sim::DriverStationSim::SetAutonomous(false); + frc::sim::DriverStationSim::SetEnabled(true); + frc::sim::DriverStationSim::NotifyNewData(); + + frc::sim::StepTiming(135_s); + + // end of match + frc::sim::DriverStationSim::SetAutonomous(false); + frc::sim::DriverStationSim::SetEnabled(false); + frc::sim::DriverStationSim::NotifyNewData(); +} diff --git a/wpilibcExamples/src/test/cpp/examples/RamseteCommand/cpp/main.cpp b/wpilibcExamples/src/test/cpp/examples/RamseteCommand/cpp/main.cpp new file mode 100644 index 00000000000..38d5cfa2fed --- /dev/null +++ b/wpilibcExamples/src/test/cpp/examples/RamseteCommand/cpp/main.cpp @@ -0,0 +1,16 @@ +// 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 +#include + +/** + * Runs all unit tests. + */ +int main(int argc, char** argv) { + HAL_Initialize(500, 0); + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/wpilibcExamples/src/test/cpp/examples/SwerveControllerCommand/cpp/SwerveControllerCommandTest.cpp b/wpilibcExamples/src/test/cpp/examples/SwerveControllerCommand/cpp/SwerveControllerCommandTest.cpp new file mode 100644 index 00000000000..eb20804de01 --- /dev/null +++ b/wpilibcExamples/src/test/cpp/examples/SwerveControllerCommand/cpp/SwerveControllerCommandTest.cpp @@ -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 +#include + +#include +#include +#include +#include + +#include "Robot.h" + +class SwerveControllerCommandTest : public testing::Test { + Robot m_robot; + std::optional m_thread; + bool joystickWarning; + + public: + void SetUp() override { + frc::sim::PauseTiming(); + joystickWarning = frc::DriverStation::IsJoystickConnectionWarningSilenced(); + frc::DriverStation::SilenceJoystickConnectionWarning(true); + + m_thread = std::thread([&] { m_robot.StartCompetition(); }); + frc::sim::StepTiming(0.0_ms); // Wait for Notifiers + } + + void TearDown() override { + m_robot.EndCompetition(); + m_thread->join(); + + frc::sim::DriverStationSim::ResetData(); + frc::DriverStation::SilenceJoystickConnectionWarning(joystickWarning); + } +}; + +TEST_F(SwerveControllerCommandTest, Match) { + std::cerr << "autonomous" << std::endl; + // auto + frc::sim::DriverStationSim::SetAutonomous(true); + frc::sim::DriverStationSim::SetEnabled(true); + frc::sim::DriverStationSim::NotifyNewData(); + + frc::sim::StepTiming(15_s); + + // brief disabled period- exact duration shouldn't matter + std::cerr << "mid disabled" << std::endl; + frc::sim::DriverStationSim::SetAutonomous(false); + frc::sim::DriverStationSim::SetEnabled(false); + frc::sim::DriverStationSim::NotifyNewData(); + + frc::sim::StepTiming(3_s); + + // teleop + std::cerr << "teleop" << std::endl; + frc::sim::DriverStationSim::SetAutonomous(false); + frc::sim::DriverStationSim::SetEnabled(true); + frc::sim::DriverStationSim::NotifyNewData(); + + frc::sim::StepTiming(135_s); + + // end of match + std::cerr << "end of match" << std::endl; + frc::sim::DriverStationSim::SetAutonomous(false); + frc::sim::DriverStationSim::SetEnabled(false); + frc::sim::DriverStationSim::NotifyNewData(); +} diff --git a/wpilibcExamples/src/test/cpp/examples/SwerveControllerCommand/cpp/main.cpp b/wpilibcExamples/src/test/cpp/examples/SwerveControllerCommand/cpp/main.cpp new file mode 100644 index 00000000000..38d5cfa2fed --- /dev/null +++ b/wpilibcExamples/src/test/cpp/examples/SwerveControllerCommand/cpp/main.cpp @@ -0,0 +1,16 @@ +// 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 +#include + +/** + * Runs all unit tests. + */ +int main(int argc, char** argv) { + HAL_Initialize(500, 0); + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/wpilibcIntegrationTests/src/main/native/cpp/MotorEncoderTest.cpp b/wpilibcIntegrationTests/src/main/native/cpp/MotorEncoderTest.cpp index be7efb8e68e..949950472ca 100644 --- a/wpilibcIntegrationTests/src/main/native/cpp/MotorEncoderTest.cpp +++ b/wpilibcIntegrationTests/src/main/native/cpp/MotorEncoderTest.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "TestBench.h" #include "frc/Encoder.h" @@ -37,6 +38,8 @@ std::ostream& operator<<(std::ostream& os, MotorEncoderTestType const& type) { static constexpr auto kMotorTime = 0.5_s; +WPI_IGNORE_DEPRECATED + /** * A fixture that includes a PWM motor controller and an encoder connected to * the same motor. @@ -197,3 +200,5 @@ TEST_P(MotorEncoderTest, Reset) { INSTANTIATE_TEST_SUITE_P(Tests, MotorEncoderTest, testing::Values(TEST_VICTOR, TEST_JAGUAR, TEST_TALON)); + +WPI_UNIGNORE_DEPRECATED diff --git a/wpilibcIntegrationTests/src/main/native/cpp/MotorInvertingTest.cpp b/wpilibcIntegrationTests/src/main/native/cpp/MotorInvertingTest.cpp index 870f029f9d7..49a419c10dc 100644 --- a/wpilibcIntegrationTests/src/main/native/cpp/MotorInvertingTest.cpp +++ b/wpilibcIntegrationTests/src/main/native/cpp/MotorInvertingTest.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "TestBench.h" #include "frc/Encoder.h" @@ -32,6 +33,9 @@ std::ostream& operator<<(std::ostream& os, MotorInvertingTestType const& type) { return os; } + +WPI_IGNORE_DEPRECATED + class MotorInvertingTest : public testing::TestWithParam { protected: @@ -153,3 +157,5 @@ TEST_P(MotorInvertingTest, InvertingSwitchingNegToPos) { INSTANTIATE_TEST_SUITE_P(Tests, MotorInvertingTest, testing::Values(TEST_VICTOR, TEST_JAGUAR, TEST_TALON)); + +WPI_UNIGNORE_DEPRECATED diff --git a/wpilibj/src/generate/WPILibVersion.java.in b/wpilibj/src/generate/WPILibVersion.java.in index cc5e6b084d3..f38d1b0b721 100644 --- a/wpilibj/src/generate/WPILibVersion.java.in +++ b/wpilibj/src/generate/WPILibVersion.java.in @@ -1,10 +1,13 @@ package edu.wpi.first.wpilibj.util; -/* +/** * 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. */ - public final class WPILibVersion { + /** The version number. */ public static final String Version = "${wpilib_version}"; + + /** Utility class. */ + private WPILibVersion() {} } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16448_IMU.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16448_IMU.java index 0b11a447682..93260c0f2d2 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16448_IMU.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16448_IMU.java @@ -94,18 +94,31 @@ public class ADIS16448_IMU implements AutoCloseable, Sendable { private static final int PROD_ID = 0x56; // Product identifier private static final int SERIAL_NUM = 0x58; // Lot-specific serial number + /** ADIS16448 calibration times. */ public enum CalibrationTime { + /** 32 ms calibration time */ _32ms(0), + /** 64 ms calibration time */ _64ms(1), + /** 128 ms calibration time */ _128ms(2), + /** 256 ms calibration time */ _256ms(3), + /** 512 ms calibration time */ _512ms(4), + /** 1 s calibration time */ _1s(5), + /** 2 s calibration time */ _2s(6), + /** 4 s calibration time */ _4s(7), + /** 8 s calibration time */ _8s(8), + /** 16 s calibration time */ _16s(9), + /** 32 s calibration time */ _32s(10), + /** 64 s calibration time */ _64s(11); private final int value; @@ -115,9 +128,13 @@ public enum CalibrationTime { } } + /** IMU axes. */ public enum IMUAxis { + /** The IMU's X axis. */ kX, + /** The IMU's Y axis. */ kY, + /** The IMU's Z axis. */ kZ } @@ -248,11 +265,14 @@ public void run() { } } + /** Creates a new ADIS16448_IMU object. */ public ADIS16448_IMU() { this(IMUAxis.kZ, SPI.Port.kMXP, CalibrationTime._512ms); } /** + * Creates a new ADIS16448_IMU object. + * * @param yaw_axis The axis that measures the yaw * @param port The SPI Port the gyro is plugged into * @param cal_time Calibration time @@ -332,6 +352,11 @@ public ADIS16448_IMU(final IMUAxis yaw_axis, SPI.Port port, CalibrationTime cal_ m_connected = true; } + /** + * Checks the connection status of the IMU. + * + * @return True if the IMU is connected, false otherwise. + */ public boolean isConnected() { if (m_simConnected != null) { return m_simConnected.get(); @@ -339,7 +364,6 @@ public boolean isConnected() { return m_connected; } - /** */ private static int toUShort(byte[] buf) { return (((buf[0] & 0xFF) << 8) + ((buf[1] & 0xFF) << 0)); } @@ -348,7 +372,7 @@ private static int toUByte(int... buf) { return (buf[0] & 0xFF); } - public static int toUShort(int... buf) { + private static int toUShort(int... buf) { return (((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF)); } @@ -359,7 +383,7 @@ private static long toULong(int sint) { /** */ private static int toShort(int... buf) { - return (short) (((buf[0] & 0xFF) << 8) + ((buf[1] & 0xFF))); + return (short) (((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF)); } /** */ @@ -481,7 +505,18 @@ boolean switchToAutoSPI() { return true; } - public int configDecRate(int m_decRate) { + /** + * Configures the decimation rate of the IMU. + * + * @param decimationRate The new decimation value. + * @return 0 if success, 1 if no change, 2 if error. + */ + public int configDecRate(int decimationRate) { + // Switches the active SPI port to standard SPI mode, writes a new value to + // the DECIMATE register in the IMU, and re-enables auto SPI. + // + // This function enters standard SPI mode, writes a new DECIMATE setting to + // the IMU, adjusts the sample scale factor, and re-enters auto SPI mode. int writeValue; int readbackValue; if (!switchToStandardSPI()) { @@ -490,19 +525,19 @@ public int configDecRate(int m_decRate) { } /* Check max */ - if (m_decRate > 9) { + if (decimationRate > 9) { DriverStation.reportError( "Attempted to write an invalid decimation value. Capping at 9", false); - m_decRate = 9; + decimationRate = 9; } - if (m_decRate < 0) { + if (decimationRate < 0) { DriverStation.reportError( "Attempted to write an invalid decimation value. Capping at 0", false); - m_decRate = 0; + decimationRate = 0; } /* Shift decimation setting to correct position and select internal sync */ - writeValue = (m_decRate << 8) | 0x1; + writeValue = (decimationRate << 8) | 0x1; /* Apply to IMU */ writeRegister(SMPL_PRD, writeValue); @@ -624,6 +659,12 @@ private void writeRegister(final int reg, final int val) { m_spi.write(buf, 2); } + /** + * Reset the gyro. + * + *

Resets the gyro accumulations to a heading of zero. This can be used if there is significant + * drift in the gyro and it needs to be recalibrated after running. + */ public void reset() { synchronized (this) { m_integ_gyro_angle_x = 0.0; @@ -957,7 +998,9 @@ private double compFilterProcess(double compAngle, double accelAngle, double ome } /** - * @return Yaw axis angle in degrees (CCW positive) + * Returns the yaw axis angle in degrees (CCW positive). + * + * @return Yaw axis angle in degrees (CCW positive). */ public synchronized double getAngle() { switch (m_yaw_axis) { @@ -973,7 +1016,9 @@ public synchronized double getAngle() { } /** - * @return Yaw axis angular rate in degrees per second (CCW positive) + * Returns the yaw axis angular rate in degrees per second (CCW positive). + * + * @return Yaw axis angular rate in degrees per second (CCW positive). */ public synchronized double getRate() { switch (m_yaw_axis) { @@ -989,14 +1034,18 @@ public synchronized double getRate() { } /** - * @return Yaw Axis + * Returns which axis, kX, kY, or kZ, is set to the yaw axis. + * + * @return IMUAxis Yaw Axis */ public IMUAxis getYawAxis() { return m_yaw_axis; } /** - * @return accumulated gyro angle in the X axis in degrees + * Returns the accumulated gyro angle in the X axis in degrees. + * + * @return The accumulated gyro angle in the X axis in degrees. */ public synchronized double getGyroAngleX() { if (m_simGyroAngleX != null) { @@ -1006,7 +1055,9 @@ public synchronized double getGyroAngleX() { } /** - * @return accumulated gyro angle in the Y axis in degrees + * Returns the accumulated gyro angle in the Y axis in degrees. + * + * @return The accumulated gyro angle in the Y axis in degrees. */ public synchronized double getGyroAngleY() { if (m_simGyroAngleY != null) { @@ -1016,7 +1067,9 @@ public synchronized double getGyroAngleY() { } /** - * @return accumulated gyro angle in the Z axis in degrees + * Returns the accumulated gyro angle in the Z axis in degrees. + * + * @return The accumulated gyro angle in the Z axis in degrees. */ public synchronized double getGyroAngleZ() { if (m_simGyroAngleZ != null) { @@ -1026,7 +1079,9 @@ public synchronized double getGyroAngleZ() { } /** - * @return gyro angular rate in the X axis in degrees per second + * Returns the gyro angular rate in the X axis in degrees per second. + * + * @return The gyro angular rate in the X axis in degrees per second. */ public synchronized double getGyroRateX() { if (m_simGyroRateX != null) { @@ -1036,7 +1091,9 @@ public synchronized double getGyroRateX() { } /** - * @return gyro angular rate in the Y axis in degrees per second + * Returns the gyro angular rate in the Y axis in degrees per second. + * + * @return The gyro angular rate in the Y axis in degrees per second. */ public synchronized double getGyroRateY() { if (m_simGyroRateY != null) { @@ -1046,7 +1103,9 @@ public synchronized double getGyroRateY() { } /** - * @return gyro angular rate in the Z axis in degrees per second + * Returns the gyro angular rate in the Z axis in degrees per second. + * + * @return The gyro angular rate in the Z axis in degrees per second. */ public synchronized double getGyroRateZ() { if (m_simGyroRateZ != null) { @@ -1056,7 +1115,9 @@ public synchronized double getGyroRateZ() { } /** - * @return urrent acceleration in the X axis in meters per second squared + * Returns the acceleration in the X axis in meters per second squared. + * + * @return The acceleration in the X axis in meters per second squared. */ public synchronized double getAccelX() { if (m_simAccelX != null) { @@ -1066,7 +1127,9 @@ public synchronized double getAccelX() { } /** - * @return current acceleration in the Y axis in meters per second squared + * Returns the acceleration in the Y axis in meters per second squared. + * + * @return The acceleration in the Y axis in meters per second squared. */ public synchronized double getAccelY() { if (m_simAccelY != null) { @@ -1076,7 +1139,9 @@ public synchronized double getAccelY() { } /** - * @return current acceleration in the Z axis in meters per second squared + * Returns the acceleration in the Z axis in meters per second squared. + * + * @return The acceleration in the Z axis in meters per second squared. */ public synchronized double getAccelZ() { if (m_simAccelZ != null) { @@ -1086,7 +1151,9 @@ public synchronized double getAccelZ() { } /** - * @return Magnetic field strength in the X axis in Tesla + * Returns the magnetic field strength in the X axis in Tesla. + * + * @return The magnetic field strength in the X axis in Tesla. */ public synchronized double getMagneticFieldX() { // mG to T @@ -1094,7 +1161,9 @@ public synchronized double getMagneticFieldX() { } /** - * @return Magnetic field strength in the Y axis in Tesla + * Returns the magnetic field strength in the Y axis in Tesla. + * + * @return The magnetic field strength in the Y axis in Tesla. */ public synchronized double getMagneticFieldY() { // mG to T @@ -1102,7 +1171,9 @@ public synchronized double getMagneticFieldY() { } /** - * @return Magnetic field strength in the Z axis in Tesla + * Returns the magnetic field strength in the Z axis in Tesla. + * + * @return The magnetic field strength in the Z axis in Tesla. */ public synchronized double getMagneticFieldZ() { // mG to T @@ -1110,35 +1181,47 @@ public synchronized double getMagneticFieldZ() { } /** - * @return X-axis complementary angle in degrees + * Returns the complementary angle around the X axis computed from accelerometer and gyro rate + * measurements. + * + * @return The X-axis complementary angle in degrees. */ public synchronized double getXComplementaryAngle() { return m_compAngleX; } /** - * @return Y-axis complementary angle in degrees + * Returns the complementary angle around the Y axis computed from accelerometer and gyro rate + * measurements. + * + * @return The Y-axis complementary angle in degrees. */ public synchronized double getYComplementaryAngle() { return m_compAngleY; } /** - * @return X-axis filtered acceleration angle in degrees + * Returns the X-axis filtered acceleration angle in degrees. + * + * @return The X-axis filtered acceleration angle in degrees. */ public synchronized double getXFilteredAccelAngle() { return m_accelAngleX; } /** - * @return Y-axis filtered acceleration angle in degrees + * Returns the Y-axis filtered acceleration angle in degrees. + * + * @return The Y-axis filtered acceleration angle in degrees. */ public synchronized double getYFilteredAccelAngle() { return m_accelAngleY; } /** - * @return Barometric Pressure in PSI + * Returns the barometric pressure in PSI. + * + * @return The barometric pressure in PSI. */ public synchronized double getBarometricPressure() { // mbar to PSI conversion @@ -1146,7 +1229,9 @@ public synchronized double getBarometricPressure() { } /** - * @return Temperature in degrees Celsius + * Returns the temperature in degrees Celsius. + * + * @return The temperature in degrees Celsius. */ public synchronized double getTemperature() { return m_temp; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16470_IMU.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16470_IMU.java index 8aec21b74ed..23b6538c3c7 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16470_IMU.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADIS16470_IMU.java @@ -155,31 +155,31 @@ public class ADIS16470_IMU implements AutoCloseable, Sendable { FLASH_CNT }; - /** Calibration times for the ADIS16470. */ + /** ADIS16470 calibration times. */ public enum CalibrationTime { - /** 32ms calibration */ + /** 32 ms calibration time */ _32ms(0), - /** 64ms calibration */ + /** 64 ms calibration time */ _64ms(1), - /** 128ms calibration */ + /** 128 ms calibration time */ _128ms(2), - /** 256ms calibration */ + /** 256 ms calibration time */ _256ms(3), - /** 512ms calibration */ + /** 512 ms calibration time */ _512ms(4), - /** 1 second calibration */ + /** 1 s calibration time */ _1s(5), - /** 2 second calibration */ + /** 2 s calibration time */ _2s(6), - /** 4 second calibration */ + /** 4 s calibration time */ _4s(7), - /** 8 second calibration */ + /** 8 s calibration time */ _8s(8), - /** 16 second calibration */ + /** 16 s calibration time */ _16s(9), - /** 32 second calibration */ + /** 32 s calibration time */ _32s(10), - /** 64 second calibration */ + /** 64 s calibration time */ _64s(11); private final int value; @@ -202,11 +202,11 @@ public enum IMUAxis { kY, /** The IMU's Z axis */ kZ, - /** The user configured yaw axis */ + /** The user-configured yaw axis */ kYaw, - /** The user configured pitch axis */ + /** The user-configured pitch axis */ kPitch, - /** The user configured roll axis */ + /** The user-configured roll axis */ kRoll, } @@ -290,19 +290,22 @@ public void run() { } /** - * Creates a new ADIS16740 IMU object. The default setup is the onboard SPI port with a - * calibration time of 4 seconds. Yaw, pitch, and roll are kZ, kX, and kY respectively. + * Creates a new ADIS16740 IMU object. + * + *

The default setup is the onboard SPI port with a calibration time of 4 seconds. Yaw, pitch, + * and roll are kZ, kX, and kY respectively. */ public ADIS16470_IMU() { this(IMUAxis.kZ, IMUAxis.kX, IMUAxis.kY, SPI.Port.kOnboardCS0, CalibrationTime._4s); } /** - * Creates a new ADIS16740 IMU object. The default setup is the onboard SPI port with a - * calibration time of 4 seconds. + * Creates a new ADIS16740 IMU object. + * + *

The default setup is the onboard SPI port with a calibration time of 4 seconds. * *

Input axes limited to kX, kY and kZ. Specifying kYaw, kPitch,or kRoll will result in - * an error. + * an error. * * @param yaw_axis The axis that measures the yaw * @param pitch_axis The axis that measures the pitch @@ -315,8 +318,8 @@ public ADIS16470_IMU(IMUAxis yaw_axis, IMUAxis pitch_axis, IMUAxis roll_axis) { /** * Creates a new ADIS16740 IMU object. * - *

Input axes limited to kX, kY and kZ. Specifying kYaw, kPitch,or kRoll will result in - * an error. + *

Input axes limited to kX, kY and kZ. Specifying kYaw, kPitch,or kRoll will result in + * an error. * * @param yaw_axis The axis that measures the yaw * @param pitch_axis The axis that measures the pitch @@ -450,7 +453,7 @@ public boolean isConnected() { * @return */ private static int toUShort(ByteBuffer buf) { - return (buf.getShort(0)) & 0xFFFF; + return buf.getShort(0) & 0xFFFF; } /** @@ -466,7 +469,7 @@ private static long toULong(int sint) { * @return */ private static int toShort(int... buf) { - return (short) (((buf[0] & 0xFF) << 8) + ((buf[1] & 0xFF))); + return (short) (((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF)); } /** @@ -625,21 +628,25 @@ public int configCalTime(CalibrationTime new_cal_time) { /** * Configures the decimation rate of the IMU. * - * @param reg The new decimation value. - * @return 0 if OK, 2 if error + * @param decimationRate The new decimation value. + * @return 0 if success, 1 if no change, 2 if error. */ - public int configDecRate(int reg) { - int m_reg = reg; + public int configDecRate(int decimationRate) { + // Switches the active SPI port to standard SPI mode, writes a new value to + // the DECIMATE register in the IMU, and re-enables auto SPI. + // + // This function enters standard SPI mode, writes a new DECIMATE setting to + // the IMU, adjusts the sample scale factor, and re-enters auto SPI mode. if (!switchToStandardSPI()) { DriverStation.reportError("Failed to configure/reconfigure standard SPI.", false); return 2; } - if (m_reg > 1999) { + if (decimationRate > 1999) { DriverStation.reportError("Attempted to write an invalid decimation value.", false); - m_reg = 1999; + decimationRate = 1999; } - m_scaled_sample_rate = (((m_reg + 1.0) / 2000.0) * 1000000.0); - writeRegister(DEC_RATE, m_reg); + m_scaled_sample_rate = (((decimationRate + 1.0) / 2000.0) * 1000000.0); + writeRegister(DEC_RATE, decimationRate); System.out.println("Decimation register: " + readRegister(DEC_RATE)); if (!switchToAutoSPI()) { DriverStation.reportError("Failed to configure/reconfigure auto SPI.", false); @@ -686,7 +693,7 @@ private int readRegister(int reg) { private void writeRegister(int reg, int val) { ByteBuffer buf = ByteBuffer.allocateDirect(2); // low byte - buf.put(0, (byte) ((0x80 | reg))); + buf.put(0, (byte) (0x80 | reg)); buf.put(1, (byte) (val & 0xff)); m_spi.write(buf, 2); // high byte @@ -1003,8 +1010,10 @@ private double compFilterProcess(double compAngle, double accelAngle, double ome } /** - * Resets the gyro accumulations to a heading of zero. This can be used if the "zero" orientation - * of the sensor needs to be changed in runtime. + * Reset the gyro. + * + *

Resets the gyro accumulations to a heading of zero. This can be used if there is significant + * drift in the gyro and it needs to be recalibrated after running. */ public void reset() { synchronized (this) { @@ -1086,8 +1095,10 @@ public void setGyroAngleZ(double angle) { } /** - * @param axis The IMUAxis whose angle to return - * @return The axis angle in degrees (CCW positive) + * Returns the axis angle in degrees (CCW positive). + * + * @param axis The IMUAxis whose angle to return. + * @return The axis angle in degrees (CCW positive). */ public synchronized double getAngle(IMUAxis axis) { switch (axis) { @@ -1126,8 +1137,37 @@ public synchronized double getAngle(IMUAxis axis) { } /** - * @param axis The IMUAxis whose rate to return - * @return Axis angular rate in degrees per second (CCW positive) + * Returns the Yaw axis angle in degrees (CCW positive). + * + * @return The Yaw axis angle in degrees (CCW positive). + */ + public synchronized double getAngle() { + switch (m_yaw_axis) { + case kX: + if (m_simGyroAngleX != null) { + return m_simGyroAngleX.get(); + } + return m_integ_angle_x; + case kY: + if (m_simGyroAngleY != null) { + return m_simGyroAngleY.get(); + } + return m_integ_angle_y; + case kZ: + if (m_simGyroAngleZ != null) { + return m_simGyroAngleZ.get(); + } + return m_integ_angle_z; + default: + } + return 0.0; + } + + /** + * Returns the axis angular rate in degrees per second (CCW positive). + * + * @param axis The IMUAxis whose rate to return. + * @return Axis angular rate in degrees per second (CCW positive). */ public synchronized double getRate(IMUAxis axis) { switch (axis) { @@ -1164,6 +1204,33 @@ public synchronized double getRate(IMUAxis axis) { return 0.0; } + /** + * Returns the Yaw axis angular rate in degrees per second (CCW positive). + * + * @return Yaw axis angular rate in degrees per second (CCW positive). + */ + public synchronized double getRate() { + switch (m_yaw_axis) { + case kX: + if (m_simGyroRateX != null) { + return m_simGyroRateX.get(); + } + return m_gyro_rate_x; + case kY: + if (m_simGyroRateY != null) { + return m_simGyroRateY.get(); + } + return m_gyro_rate_y; + case kZ: + if (m_simGyroRateZ != null) { + return m_simGyroRateZ.get(); + } + return m_gyro_rate_z; + default: + } + return 0.0; + } + /** * Returns which axis, kX, kY, or kZ, is set to the yaw axis. * @@ -1192,49 +1259,65 @@ public IMUAxis getRollAxis() { } /** - * @return The acceleration in the X axis + * Returns the acceleration in the X axis in meters per second squared. + * + * @return The acceleration in the X axis in meters per second squared. */ public synchronized double getAccelX() { return m_accel_x * 9.81; } /** - * @return The acceleration in the Y axis + * Returns the acceleration in the Y axis in meters per second squared. + * + * @return The acceleration in the Y axis in meters per second squared. */ public synchronized double getAccelY() { return m_accel_y * 9.81; } /** - * @return The acceleration in the Z axis + * Returns the acceleration in the Z axis in meters per second squared. + * + * @return The acceleration in the Z axis in meters per second squared. */ public synchronized double getAccelZ() { return m_accel_z * 9.81; } /** - * @return The X-axis complementary angle + * Returns the complementary angle around the X axis computed from accelerometer and gyro rate + * measurements. + * + * @return The X-axis complementary angle in degrees. */ public synchronized double getXComplementaryAngle() { return m_compAngleX; } /** - * @return The Y-axis complementary angle + * Returns the complementary angle around the Y axis computed from accelerometer and gyro rate + * measurements. + * + * @return The Y-axis complementary angle in degrees. */ public synchronized double getYComplementaryAngle() { return m_compAngleY; } /** - * @return The X-axis filtered acceleration angle + * Returns the X-axis filtered acceleration angle in degrees. + * + * @return The X-axis filtered acceleration angle in degrees. */ public synchronized double getXFilteredAccelAngle() { return m_accelAngleX; } /** - * @return The Y-axis filtered acceleration angle + * Returns the Y-axis filtered acceleration angle in degrees. + * + * @return The Y-axis filtered acceleration angle in degrees. */ public synchronized double getYFilteredAccelAngle() { return m_accelAngleY; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java index e96151bd769..357618b7f4a 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java @@ -25,34 +25,46 @@ * href="https://docs.wpilib.org/en/stable/docs/yearly-overview/known-issues.html#onboard-i2c-causing-system-lockups"> * WPILib Known Issues page for details. */ -@SuppressWarnings({"TypeName", "PMD.UnusedPrivateField"}) +@SuppressWarnings("TypeName") public class ADXL345_I2C implements NTSendable, AutoCloseable { - private static final byte kAddress = 0x1D; + /** Default I2C device address. */ + public static final byte kAddress = 0x1D; + private static final byte kPowerCtlRegister = 0x2D; private static final byte kDataFormatRegister = 0x31; private static final byte kDataRegister = 0x32; private static final double kGsPerLSB = 0.00390625; - private static final byte kPowerCtl_Link = 0x20; - private static final byte kPowerCtl_AutoSleep = 0x10; + // private static final byte kPowerCtl_Link = 0x20; + // private static final byte kPowerCtl_AutoSleep = 0x10; private static final byte kPowerCtl_Measure = 0x08; - private static final byte kPowerCtl_Sleep = 0x04; + // private static final byte kPowerCtl_Sleep = 0x04; - private static final byte kDataFormat_SelfTest = (byte) 0x80; - private static final byte kDataFormat_SPI = 0x40; - private static final byte kDataFormat_IntInvert = 0x20; + // private static final byte kDataFormat_SelfTest = (byte) 0x80; + // private static final byte kDataFormat_SPI = 0x40; + // private static final byte kDataFormat_IntInvert = 0x20; private static final byte kDataFormat_FullRes = 0x08; - private static final byte kDataFormat_Justify = 0x04; + // private static final byte kDataFormat_Justify = 0x04; + + /** Accelerometer range. */ public enum Range { + /** 2 Gs max. */ k2G, + /** 4 Gs max. */ k4G, + /** 8 Gs max. */ k8G, + /** 16 Gs max. */ k16G } + /** Accelerometer axes. */ public enum Axes { + /** X axis. */ kX((byte) 0x00), + /** Y axis. */ kY((byte) 0x02), + /** Z axis. */ kZ((byte) 0x04); /** The integer value representing this enumeration. */ @@ -63,20 +75,29 @@ public enum Axes { } } + /** Container type for accelerations from all axes. */ @SuppressWarnings("MemberName") public static class AllAxes { + /** Acceleration along the X axis in g-forces. */ public double XAxis; + + /** Acceleration along the Y axis in g-forces. */ public double YAxis; + + /** Acceleration along the Z axis in g-forces. */ public double ZAxis; + + /** Default constructor. */ + public AllAxes() {} } - protected I2C m_i2c; + private I2C m_i2c; - protected SimDevice m_simDevice; - protected SimEnum m_simRange; - protected SimDouble m_simX; - protected SimDouble m_simY; - protected SimDouble m_simZ; + private SimDevice m_simDevice; + private SimEnum m_simRange; + private SimDouble m_simX; + private SimDouble m_simY; + private SimDouble m_simZ; /** * Constructs the ADXL345 Accelerometer with I2C address 0x1D. @@ -123,10 +144,20 @@ public ADXL345_I2C(I2C.Port port, Range range, int deviceAddress) { SendableRegistry.addLW(this, "ADXL345_I2C", port.value); } + /** + * Returns the I2C port. + * + * @return The I2C port. + */ public int getPort() { return m_i2c.getPort(); } + /** + * Returns the I2C device address. + * + * @return The I2C device address. + */ public int getDeviceAddress() { return m_i2c.getDeviceAddress(); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java index b98b917f431..29a20cbd5c3 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java @@ -19,7 +19,7 @@ import java.nio.ByteOrder; /** ADXL345 SPI Accelerometer. */ -@SuppressWarnings({"TypeName", "PMD.UnusedPrivateField"}) +@SuppressWarnings("TypeName") public class ADXL345_SPI implements NTSendable, AutoCloseable { private static final int kPowerCtlRegister = 0x2D; private static final int kDataFormatRegister = 0x31; @@ -29,27 +29,37 @@ public class ADXL345_SPI implements NTSendable, AutoCloseable { private static final int kAddress_Read = 0x80; private static final int kAddress_MultiByte = 0x40; - private static final int kPowerCtl_Link = 0x20; - private static final int kPowerCtl_AutoSleep = 0x10; + // private static final int kPowerCtl_Link = 0x20; + // private static final int kPowerCtl_AutoSleep = 0x10; private static final int kPowerCtl_Measure = 0x08; - private static final int kPowerCtl_Sleep = 0x04; + // private static final int kPowerCtl_Sleep = 0x04; - private static final int kDataFormat_SelfTest = 0x80; - private static final int kDataFormat_SPI = 0x40; - private static final int kDataFormat_IntInvert = 0x20; + // private static final int kDataFormat_SelfTest = 0x80; + // private static final int kDataFormat_SPI = 0x40; + // private static final int kDataFormat_IntInvert = 0x20; private static final int kDataFormat_FullRes = 0x08; - private static final int kDataFormat_Justify = 0x04; + // private static final int kDataFormat_Justify = 0x04; + + /** Accelerometer range. */ public enum Range { + /** 2 Gs max. */ k2G, + /** 4 Gs max. */ k4G, + /** 8 Gs max. */ k8G, + /** 16 Gs max. */ k16G } + /** Accelerometer axes. */ public enum Axes { + /** X axis. */ kX((byte) 0x00), + /** Y axis. */ kY((byte) 0x02), + /** Z axis. */ kZ((byte) 0x04); /** The integer value representing this enumeration. */ @@ -60,20 +70,29 @@ public enum Axes { } } + /** Container type for accelerations from all axes. */ @SuppressWarnings("MemberName") public static class AllAxes { + /** Acceleration along the X axis in g-forces. */ public double XAxis; + + /** Acceleration along the Y axis in g-forces. */ public double YAxis; + + /** Acceleration along the Z axis in g-forces. */ public double ZAxis; + + /** Default constructor. */ + public AllAxes() {} } - protected SPI m_spi; + private SPI m_spi; - protected SimDevice m_simDevice; - protected SimEnum m_simRange; - protected SimDouble m_simX; - protected SimDouble m_simY; - protected SimDouble m_simZ; + private SimDevice m_simDevice; + private SimEnum m_simRange; + private SimDouble m_simX; + private SimDouble m_simY; + private SimDouble m_simZ; /** * Constructor. @@ -102,6 +121,11 @@ public ADXL345_SPI(SPI.Port port, Range range) { SendableRegistry.addLW(this, "ADXL345_SPI", port.value); } + /** + * Returns the SPI port. + * + * @return The SPI port. + */ public int getPort() { return m_spi.getPort(); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL362.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL362.java index b4950682b16..b00ea2b8f6a 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL362.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL362.java @@ -38,22 +38,31 @@ public class ADXL362 implements NTSendable, AutoCloseable { private static final byte kPowerCtl_UltraLowNoise = 0x20; - @SuppressWarnings("PMD.UnusedPrivateField") - private static final byte kPowerCtl_AutoSleep = 0x04; + // @SuppressWarnings("PMD.UnusedPrivateField") + // private static final byte kPowerCtl_AutoSleep = 0x04; private static final byte kPowerCtl_Measure = 0x02; + /** Accelerometer range. */ public enum Range { + /** 2 Gs max. */ k2G, + /** 4 Gs max. */ k4G, + /** 8 Gs max. */ k8G } + /** Accelerometer axes. */ public enum Axes { + /** X axis. */ kX((byte) 0x00), + /** Y axis. */ kY((byte) 0x02), + /** Z axis. */ kZ((byte) 0x04); + /** Axis value. */ public final byte value; Axes(byte value) { @@ -61,11 +70,20 @@ public enum Axes { } } + /** Container type for accelerations from all axes. */ @SuppressWarnings("MemberName") public static class AllAxes { + /** Acceleration along the X axis in g-forces. */ public double XAxis; + + /** Acceleration along the Y axis in g-forces. */ public double YAxis; + + /** Acceleration along the Z axis in g-forces. */ public double ZAxis; + + /** Default constructor. */ + public AllAxes() {} } private SPI m_spi; @@ -142,6 +160,11 @@ public ADXL362(SPI.Port port, Range range) { SendableRegistry.addLW(this, "ADXL362", port.value); } + /** + * Returns the SPI port. + * + * @return The SPI port. + */ public int getPort() { return m_spi.getPort(); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java index 799d43202bf..d574a24824a 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java @@ -26,21 +26,21 @@ *

This class is for the digital ADXRS450 gyro sensor that connects via SPI. Only one instance of * an ADXRS Gyro is supported. */ -@SuppressWarnings({"TypeName", "PMD.UnusedPrivateField"}) +@SuppressWarnings("TypeName") public class ADXRS450_Gyro implements Sendable, AutoCloseable { private static final double kSamplePeriod = 0.0005; private static final double kCalibrationSampleTime = 5.0; private static final double kDegreePerSecondPerLSB = 0.0125; - private static final int kRateRegister = 0x00; - private static final int kTemRegister = 0x02; - private static final int kLoCSTRegister = 0x04; - private static final int kHiCSTRegister = 0x06; - private static final int kQuadRegister = 0x08; - private static final int kFaultRegister = 0x0A; + // private static final int kRateRegister = 0x00; + // private static final int kTemRegister = 0x02; + // private static final int kLoCSTRegister = 0x04; + // private static final int kHiCSTRegister = 0x06; + // private static final int kQuadRegister = 0x08; + // private static final int kFaultRegister = 0x0A; private static final int kPIDRegister = 0x0C; - private static final int kSNHighRegister = 0x0E; - private static final int kSNLowRegister = 0x10; + // private static final int kSNHighRegister = 0x0E; + // private static final int kSNLowRegister = 0x10; private SPI m_spi; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLEDBuffer.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLEDBuffer.java index a19773e1f8f..68644a05839 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLEDBuffer.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLEDBuffer.java @@ -131,8 +131,7 @@ public int getLength() { * @return the LED color at the specified index */ public Color8Bit getLED8Bit(int index) { - return new Color8Bit( - m_buffer[index * 4 + 2] & 0xFF, m_buffer[index * 4 + 1] & 0xFF, m_buffer[index * 4] & 0xFF); + return new Color8Bit(getRed(index), getGreen(index), getBlue(index)); } /** @@ -142,9 +141,70 @@ public Color8Bit getLED8Bit(int index) { * @return the LED color at the specified index */ public Color getLED(int index) { - return new Color( - (m_buffer[index * 4 + 2] & 0xFF) / 255.0, - (m_buffer[index * 4 + 1] & 0xFF) / 255.0, - (m_buffer[index * 4] & 0xFF) / 255.0); + return new Color(getRed(index) / 255.0, getGreen(index) / 255.0, getBlue(index) / 255.0); + } + + /** + * Gets the red channel of the color at the specified index. + * + * @param index the index of the LED to read + * @return the value of the red channel, from [0, 255] + */ + public int getRed(int index) { + return m_buffer[index * 4 + 2] & 0xFF; + } + + /** + * Gets the green channel of the color at the specified index. + * + * @param index the index of the LED to read + * @return the value of the green channel, from [0, 255] + */ + public int getGreen(int index) { + return m_buffer[index * 4 + 1] & 0xFF; + } + + /** + * Gets the blue channel of the color at the specified index. + * + * @param index the index of the LED to read + * @return the value of the blue channel, from [0, 255] + */ + public int getBlue(int index) { + return m_buffer[index * 4] & 0xFF; + } + + /** + * A functional interface that allows for iteration over an LED buffer without manually writing an + * indexed for-loop. + */ + @FunctionalInterface + public interface IndexedColorIterator { + /** + * Accepts an index of an LED in the buffer and the red, green, and blue components of the + * currently stored color for that LED. + * + * @param index the index of the LED in the buffer that the red, green, and blue channels + * corresponds to + * @param r the value of the red channel of the color currently in the buffer at index {@code i} + * @param g the value of the green channel of the color currently in the buffer at index {@code + * i} + * @param b the value of the blue channel of the color currently in the buffer at index {@code + * i} + */ + void accept(int index, int r, int g, int b); + } + + /** + * Iterates over the LEDs in the buffer, starting from index 0. The iterator function is passed + * the current index of iteration, along with the values for the red, green, and blue components + * of the color written to the LED at that index. + * + * @param iterator the iterator function to call for each LED in the buffer. + */ + public void forEach(IndexedColorIterator iterator) { + for (int i = 0; i < getLength(); i++) { + iterator.accept(i, getRed(i), getGreen(i), getBlue(i)); + } } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogEncoder.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogEncoder.java index dc8b668bffa..727ad2cd3de 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogEncoder.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogEncoder.java @@ -22,9 +22,9 @@ public class AnalogEncoder implements Sendable, AutoCloseable { private double m_distancePerRotation = 1.0; private double m_lastPosition; - protected SimDevice m_simDevice; - protected SimDouble m_simPosition; - protected SimDouble m_simAbsolutePosition; + private SimDevice m_simDevice; + private SimDouble m_simPosition; + private SimDouble m_simAbsolutePosition; /** * Construct a new AnalogEncoder attached to a specific AnalogIn channel. diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java index 198283e34f4..29a17bbed0e 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java @@ -25,7 +25,7 @@ */ public class AnalogGyro implements Sendable, AutoCloseable { private static final double kDefaultVoltsPerDegreePerSecond = 0.007; - protected AnalogInput m_analog; + private AnalogInput m_analog; private boolean m_channelAllocated; private int m_gyroHandle; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogOutput.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogOutput.java index 10f143c8fec..354cedc038d 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogOutput.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogOutput.java @@ -50,10 +50,20 @@ public int getChannel() { return m_channel; } + /** + * Set the value of the analog output. + * + * @param voltage The output value in Volts, from 0.0 to +5.0. + */ public void setVoltage(double voltage) { AnalogJNI.setAnalogOutput(m_port, voltage); } + /** + * Get the voltage of the analog output. + * + * @return The value in Volts, from 0.0 to +5.0. + */ public double getVoltage() { return AnalogJNI.getAnalogOutput(m_port); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTrigger.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTrigger.java index 77f8381608b..1d4a404dd95 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTrigger.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTrigger.java @@ -12,27 +12,18 @@ import edu.wpi.first.util.sendable.SendableBuilder; import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.AnalogTriggerOutput.AnalogTriggerType; +import java.lang.ref.Reference; /** Class for creating and configuring Analog Triggers. */ public class AnalogTrigger implements Sendable, AutoCloseable { - /** Exceptions dealing with improper operation of the Analog trigger. */ - public static class AnalogTriggerException extends RuntimeException { - /** - * Create a new exception with the given message. - * - * @param message the message to pass with the exception - */ - public AnalogTriggerException(String message) { - super(message); - } - } - /** Where the analog trigger is attached. */ protected int m_port; - protected AnalogInput m_analogInput; - protected DutyCycle m_dutyCycle; - protected boolean m_ownsAnalog; + private AnalogInput m_analogInput; + + private DutyCycle m_dutyCycle; + + private boolean m_ownsAnalog; /** * Constructor for an analog trigger given a channel number. @@ -89,6 +80,7 @@ public void close() { if (m_ownsAnalog && m_analogInput != null) { m_analogInput.close(); } + Reference.reachabilityFence(m_dutyCycle); } /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTriggerOutput.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTriggerOutput.java index c80179b5c4d..4d459fc2c43 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTriggerOutput.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTriggerOutput.java @@ -106,9 +106,13 @@ public boolean isAnalogTrigger() { /** Defines the state in which the AnalogTrigger triggers. */ public enum AnalogTriggerType { + /** In window. */ kInWindow(AnalogJNI.AnalogTriggerType.kInWindow), + /** State. */ kState(AnalogJNI.AnalogTriggerType.kState), + /** Rising pulse. */ kRisingPulse(AnalogJNI.AnalogTriggerType.kRisingPulse), + /** Falling pulse. */ kFallingPulse(AnalogJNI.AnalogTriggerType.kFallingPulse); private final int value; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/BuiltInAccelerometer.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/BuiltInAccelerometer.java index 0b2654b4782..522ab88476a 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/BuiltInAccelerometer.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/BuiltInAccelerometer.java @@ -17,9 +17,13 @@ *

This class allows access to the roboRIO's internal accelerometer. */ public class BuiltInAccelerometer implements Sendable, AutoCloseable { + /** Accelerometer range. */ public enum Range { + /** 2 Gs max. */ k2G, + /** 4 Gs max. */ k4G, + /** 8 Gs max. */ k8G } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/CAN.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/CAN.java index 8808a4b68b6..df0be482c26 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/CAN.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/CAN.java @@ -5,6 +5,7 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.CANAPIJNI; +import edu.wpi.first.hal.CANAPITypes; import edu.wpi.first.hal.CANData; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; @@ -21,8 +22,11 @@ * calls. */ public class CAN implements Closeable { - public static final int kTeamManufacturer = 8; - public static final int kTeamDeviceType = 10; + /** Team manufacturer. */ + public static final int kTeamManufacturer = CANAPITypes.CANManufacturer.kTeamUse.id; + + /** Team device type. */ + public static final int kTeamDeviceType = CANAPITypes.CANDeviceType.kMiscellaneous.id; private final int m_handle; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/CompressorConfigType.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/CompressorConfigType.java index 48bb81ffcdb..7735cdd579c 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/CompressorConfigType.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/CompressorConfigType.java @@ -6,12 +6,18 @@ import edu.wpi.first.hal.REVPHJNI; +/** Compressor config type. */ public enum CompressorConfigType { + /** Disabled. */ Disabled(REVPHJNI.COMPRESSOR_CONFIG_TYPE_DISABLED), + /** Digital. */ Digital(REVPHJNI.COMPRESSOR_CONFIG_TYPE_DIGITAL), + /** Analog. */ Analog(REVPHJNI.COMPRESSOR_CONFIG_TYPE_ANALOG), + /** Hybrid. */ Hybrid(REVPHJNI.COMPRESSOR_CONFIG_TYPE_HYBRID); + /** CompressorConfigType value. */ public final int value; CompressorConfigType(int value) { @@ -37,6 +43,11 @@ public static CompressorConfigType fromValue(int value) { } } + /** + * Returns the CompressorConfigType's value. + * + * @return The CompressorConfigType's value. + */ public int getValue() { return value; } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java index 26f921b3f4d..eca3dd5a8ff 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java @@ -38,6 +38,7 @@ public enum Mode { /** mode: external direction. */ kExternalDirection(3); + /** Mode value. */ public final int value; Mode(int value) { @@ -45,13 +46,23 @@ public enum Mode { } } - protected DigitalSource m_upSource; // /< What makes the counter count up. - protected DigitalSource m_downSource; // /< What makes the counter count down. + /** What makes the counter count up. */ + protected DigitalSource m_upSource; + + /** What makes the counter count down. */ + protected DigitalSource m_downSource; + private boolean m_allocatedUpSource; private boolean m_allocatedDownSource; - int m_counter; // /< The FPGA counter object. - private int m_index; // /< The index of this counter. - private double m_distancePerPulse = 1; // distance of travel for each tick + + /** The FPGA counter object. */ + int m_counter; + + /** The index of this counter. */ + private int m_index; + + /** Distance of travel for each tick. */ + private double m_distancePerPulse = 1; /** * Create an instance of a counter with the given mode. diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/CounterBase.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/CounterBase.java index 60d56909d36..6e03b72356c 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/CounterBase.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/CounterBase.java @@ -22,6 +22,7 @@ enum EncodingType { /** Count rising and falling on both channels. */ k4X(2); + /** EncodingType value. */ public final int value; EncodingType(int value) { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMA.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMA.java index a919da55f09..bd26a7bf5de 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMA.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMA.java @@ -7,9 +7,11 @@ import edu.wpi.first.hal.DMAJNI; import edu.wpi.first.wpilibj.motorcontrol.PWMMotorController; +/** Class for configuring Direct Memory Access (DMA) of FPGA inputs. */ public class DMA implements AutoCloseable { final int m_dmaHandle; + /** Default constructor. */ public DMA() { m_dmaHandle = DMAJNI.initialize(); } @@ -19,50 +21,128 @@ public void close() { DMAJNI.free(m_dmaHandle); } + /** + * Sets whether DMA is paused. + * + * @param pause True pauses DMA. + */ public void setPause(boolean pause) { DMAJNI.setPause(m_dmaHandle, pause); } + /** + * Sets DMA to trigger at an interval. + * + * @param periodSeconds Period at which to trigger DMA in seconds. + */ public void setTimedTrigger(double periodSeconds) { DMAJNI.setTimedTrigger(m_dmaHandle, periodSeconds); } + /** + * Sets number of DMA cycles to trigger. + * + * @param cycles Number of cycles. + */ public void setTimedTriggerCycles(int cycles) { DMAJNI.setTimedTriggerCycles(m_dmaHandle, cycles); } + /** + * Adds position data for an encoder to be collected by DMA. + * + *

This can only be called if DMA is not started. + * + * @param encoder Encoder to add to DMA. + */ public void addEncoder(Encoder encoder) { DMAJNI.addEncoder(m_dmaHandle, encoder.m_encoder); } + /** + * Adds timer data for an encoder to be collected by DMA. + * + *

This can only be called if DMA is not started. + * + * @param encoder Encoder to add to DMA. + */ public void addEncoderPeriod(Encoder encoder) { DMAJNI.addEncoderPeriod(m_dmaHandle, encoder.m_encoder); } + /** + * Adds position data for an counter to be collected by DMA. + * + *

This can only be called if DMA is not started. + * + * @param counter Counter to add to DMA. + */ public void addCounter(Counter counter) { DMAJNI.addCounter(m_dmaHandle, counter.m_counter); } + /** + * Adds timer data for an counter to be collected by DMA. + * + *

This can only be called if DMA is not started. + * + * @param counter Counter to add to DMA. + */ public void addCounterPeriod(Counter counter) { DMAJNI.addCounterPeriod(m_dmaHandle, counter.m_counter); } + /** + * Adds a digital source to be collected by DMA. + * + *

This can only be called if DMA is not started. + * + * @param digitalSource DigitalSource to add to DMA. + */ public void addDigitalSource(DigitalSource digitalSource) { DMAJNI.addDigitalSource(m_dmaHandle, digitalSource.getPortHandleForRouting()); } + /** + * Adds a duty cycle input to be collected by DMA. + * + *

This can only be called if DMA is not started. + * + * @param dutyCycle DutyCycle to add to DMA. + */ public void addDutyCycle(DutyCycle dutyCycle) { DMAJNI.addDutyCycle(m_dmaHandle, dutyCycle.m_handle); } + /** + * Adds an analog input to be collected by DMA. + * + *

This can only be called if DMA is not started. + * + * @param analogInput AnalogInput to add to DMA. + */ public void addAnalogInput(AnalogInput analogInput) { DMAJNI.addAnalogInput(m_dmaHandle, analogInput.m_port); } + /** + * Adds averaged data of an analog input to be collected by DMA. + * + *

This can only be called if DMA is not started. + * + * @param analogInput AnalogInput to add to DMA. + */ public void addAveragedAnalogInput(AnalogInput analogInput) { DMAJNI.addAveragedAnalogInput(m_dmaHandle, analogInput.m_port); } + /** + * Adds accumulator data of an analog input to be collected by DMA. + * + *

This can only be called if DMA is not started. + * + * @param analogInput AnalogInput to add to DMA. + */ public void addAnalogAccumulator(AnalogInput analogInput) { DMAJNI.addAnalogAccumulator(m_dmaHandle, analogInput.m_port); } @@ -84,26 +164,58 @@ public int setExternalTrigger(DigitalSource source, boolean rising, boolean fall falling); } - public int setPwmEdgeTrigger(PWMMotorController pwm, boolean rising, boolean falling) { - return DMAJNI.setExternalTrigger(m_dmaHandle, pwm.getPwmHandle(), 0, rising, falling); - } - + /** + * Sets a DMA PWM edge trigger. + * + * @param pwm the PWM to trigger from. + * @param rising trigger on rising edge. + * @param falling trigger on falling edge. + * @return the index of the trigger + */ public int setPwmEdgeTrigger(PWM pwm, boolean rising, boolean falling) { return DMAJNI.setExternalTrigger(m_dmaHandle, pwm.getHandle(), 0, rising, falling); } + /** + * Sets a DMA PWMMotorController edge trigger. + * + * @param pwm the PWMMotorController to trigger from. + * @param rising trigger on rising edge. + * @param falling trigger on falling edge. + * @return the index of the trigger + */ + public int setPwmEdgeTrigger(PWMMotorController pwm, boolean rising, boolean falling) { + return DMAJNI.setExternalTrigger(m_dmaHandle, pwm.getPwmHandle(), 0, rising, falling); + } + + /** + * Clear all sensors from the DMA collection list. + * + *

This can only be called if DMA is not started. + */ public void clearSensors() { DMAJNI.clearSensors(m_dmaHandle); } + /** + * Clear all external triggers from the DMA trigger list. + * + *

This can only be called if DMA is not started. + */ public void clearExternalTriggers() { DMAJNI.clearExternalTriggers(m_dmaHandle); } + /** + * Starts DMA Collection. + * + * @param queueDepth The number of objects to be able to queue. + */ public void start(int queueDepth) { DMAJNI.startDMA(m_dmaHandle, queueDepth); } + /** Stops DMA Collection. */ public void stop() { DMAJNI.stopDMA(m_dmaHandle); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMASample.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMASample.java index 1055dbcc406..1435cd36d9d 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMASample.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMASample.java @@ -7,10 +7,15 @@ import edu.wpi.first.hal.AnalogJNI; import edu.wpi.first.hal.DMAJNISample; +/** DMA sample. */ public class DMASample { + /** DMA read status. */ public enum DMAReadStatus { + /** OK status. */ kOk(1), + /** Timeout status. */ kTimeout(2), + /** Error status. */ kError(3); private final int value; @@ -19,6 +24,11 @@ public enum DMAReadStatus { this.value = value; } + /** + * Returns the DMAReadStatus value. + * + * @return The DMAReadStatus value. + */ public int getValue() { return value; } @@ -41,39 +51,80 @@ public static DMAReadStatus getValue(int value) { private final DMAJNISample m_dmaSample = new DMAJNISample(); + /** Default constructor. */ + public DMASample() {} + + /** + * Retrieves a new DMA sample. + * + * @param dma DMA object. + * @param timeoutSeconds Timeout in seconds for retrieval. + * @return DMA read status. + */ public DMAReadStatus update(DMA dma, double timeoutSeconds) { return DMAReadStatus.getValue(m_dmaSample.update(dma.m_dmaHandle, timeoutSeconds)); } + /** + * Returns the DMA sample time in microseconds. + * + * @return The DMA sample time in microseconds. + */ + public long getTime() { + return m_dmaSample.getTime(); + } + + /** + * Returns the DMA sample timestamp in seconds. + * + * @return The DMA sample timestamp in seconds. + */ + public double getTimeStamp() { + return getTime() * 1.0e-6; + } + + /** + * Returns the DMA sample capture size. + * + * @return The DMA sample capture size. + */ public int getCaptureSize() { return m_dmaSample.getCaptureSize(); } + /** + * Returns the number of DMA trigger channels. + * + * @return The number of DMA trigger channels. + */ public int getTriggerChannels() { return m_dmaSample.getTriggerChannels(); } + /** + * Returns the number of remaining samples. + * + * @return The number of remaining samples. + */ public int getRemaining() { return m_dmaSample.getRemaining(); } - public long getTime() { - return m_dmaSample.getTime(); - } - - public double getTimeStamp() { - return getTime() * 1.0e-6; - } - + /** + * Returns raw encoder value from DMA. + * + * @param encoder Encoder used for DMA. + * @return Raw encoder value from DMA. + */ public int getEncoderRaw(Encoder encoder) { return m_dmaSample.getEncoder(encoder.m_encoder); } /** - * Gets the scaled encoder distance for this sample. + * Returns encoder distance from DMA. * - * @param encoder the encoder to use to read - * @return the distance + * @param encoder Encoder used for DMA. + * @return Encoder distance from DMA. */ public double getEncoderDistance(Encoder encoder) { double val = getEncoderRaw(encoder); @@ -82,43 +133,103 @@ public double getEncoderDistance(Encoder encoder) { return val; } + /** + * Returns raw encoder period from DMA. + * + * @param encoder Encoder used for DMA. + * @return Raw encoder period from DMA. + */ public int getEncoderPeriodRaw(Encoder encoder) { return m_dmaSample.getEncoderPeriod(encoder.m_encoder); } + /** + * Returns counter value from DMA. + * + * @param counter Counter used for DMA. + * @return Counter value from DMA. + */ public int getCounter(Counter counter) { return m_dmaSample.getCounter(counter.m_counter); } + /** + * Returns counter period from DMA. + * + * @param counter Counter used for DMA. + * @return Counter period from DMA. + */ public int getCounterPeriod(Counter counter) { return m_dmaSample.getCounterPeriod(counter.m_counter); } + /** + * Returns digital source value from DMA. + * + * @param digitalSource DigitalSource used for DMA. + * @return DigitalSource value from DMA. + */ public boolean getDigitalSource(DigitalSource digitalSource) { return m_dmaSample.getDigitalSource(digitalSource.getPortHandleForRouting()); } + /** + * Returns raw analog input value from DMA. + * + * @param analogInput AnalogInput used for DMA. + * @return Raw analog input value from DMA. + */ public int getAnalogInputRaw(AnalogInput analogInput) { return m_dmaSample.getAnalogInput(analogInput.m_port); } + /** + * Returns analog input voltage from DMA. + * + * @param analogInput AnalogInput used for DMA. + * @return Analog input voltage from DMA. + */ public double getAnalogInputVoltage(AnalogInput analogInput) { return AnalogJNI.getAnalogValueToVolts(analogInput.m_port, getAnalogInputRaw(analogInput)); } + /** + * Returns averaged raw analog input value from DMA. + * + * @param analogInput AnalogInput used for DMA. + * @return Averaged raw analog input value from DMA. + */ public int getAveragedAnalogInputRaw(AnalogInput analogInput) { return m_dmaSample.getAnalogInputAveraged(analogInput.m_port); } + /** + * Returns averaged analog input voltage from DMA. + * + * @param analogInput AnalogInput used for DMA. + * @return Averaged analog input voltage from DMA. + */ public double getAveragedAnalogInputVoltage(AnalogInput analogInput) { return AnalogJNI.getAnalogValueToVolts( analogInput.m_port, getAveragedAnalogInputRaw(analogInput)); } + /** + * Returns raw duty cycle output from DMA. + * + * @param dutyCycle DutyCycle used for DMA. + * @return Raw duty cycle output from DMA. + */ public int getDutyCycleOutputRaw(DutyCycle dutyCycle) { return m_dmaSample.getDutyCycleOutput(dutyCycle.m_handle); } + /** + * Returns duty cycle output (0-1) from DMA. + * + * @param dutyCycle DutyCycle used for DMA. + * @return Duty cycle output (0-1) from DMA. + */ public double getDutyCycleOutput(DutyCycle dutyCycle) { return m_dmaSample.getDutyCycleOutput(dutyCycle.m_handle) / (double) dutyCycle.getOutputScaleFactor(); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DigitalSource.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DigitalSource.java index cb8b02475bb..494efe95bf8 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DigitalSource.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DigitalSource.java @@ -11,8 +11,21 @@ * source. The source can either be a digital input or analog trigger but not both. */ public abstract class DigitalSource implements AutoCloseable { + /** Default constructor. */ + public DigitalSource() {} + + /** + * Returns true if this DigitalSource is an AnalogTrigger. + * + * @return True if this DigitalSource is an AnalogTrigger. + */ public abstract boolean isAnalogTrigger(); + /** + * The DigitalSource channel. + * + * @return The DigitalSource channel. + */ public abstract int getChannel(); /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DoubleSolenoid.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DoubleSolenoid.java index 6e037dfcf66..de0697ccdec 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DoubleSolenoid.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DoubleSolenoid.java @@ -21,8 +21,11 @@ public class DoubleSolenoid implements Sendable, AutoCloseable { /** Possible values for a DoubleSolenoid. */ public enum Value { + /** Off position. */ kOff, + /** Forward position. */ kForward, + /** Reverse position. */ kReverse } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DriverStation.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DriverStation.java index 1c6e0c8501b..cbe4da8ac30 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DriverStation.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DriverStation.java @@ -29,7 +29,7 @@ /** Provide access to the network communication data to / from the Driver Station. */ public final class DriverStation { - /** Number of Joystick Ports. */ + /** Number of Joystick ports. */ public static final int kJoystickPorts = 6; private static class HALJoystickButtons { @@ -48,6 +48,8 @@ private static class HALJoystickAxes { private static class HALJoystickAxesRaw { public int[] m_axes; + + @SuppressWarnings("unused") public int m_count; HALJoystickAxesRaw(int count) { @@ -69,14 +71,21 @@ private static class HALJoystickPOVs { /** The robot alliance that the robot is a part of. */ public enum Alliance { + /** Red alliance. */ Red, + /** Blue alliance. */ Blue } + /** The type of robot match that the robot is part of. */ public enum MatchType { + /** None. */ None, + /** Practice. */ Practice, + /** Qualification. */ Qualification, + /** Elimination. */ Elimination } @@ -1314,10 +1323,20 @@ public static void refreshData() { } } + /** + * Registers the given handle for DS data refresh notifications. + * + * @param handle The event handle. + */ public static void provideRefreshedDataEventHandle(int handle) { m_refreshEvents.add(handle); } + /** + * Unregisters the given handle from DS data refresh notifications. + * + * @param handle The event handle. + */ public static void removeRefreshedDataEventHandle(int handle) { m_refreshEvents.remove(handle); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycle.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycle.java index 2e1795c0c46..8c26d55b80b 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycle.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycle.java @@ -103,6 +103,11 @@ public final int getFPGAIndex() { return DutyCycleJNI.getFPGAIndex(m_handle); } + /** + * Get the channel of the source. + * + * @return the source channel + */ public int getSourceChannel() { return m_source.getChannel(); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycleEncoder.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycleEncoder.java index 9c54dc1c9ba..a0858cef922 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycleEncoder.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycleEncoder.java @@ -30,11 +30,11 @@ public class DutyCycleEncoder implements Sendable, AutoCloseable { private double m_sensorMin; private double m_sensorMax = 1.0; - protected SimDevice m_simDevice; - protected SimDouble m_simPosition; - protected SimDouble m_simAbsolutePosition; - protected SimDouble m_simDistancePerRotation; - protected SimBoolean m_simIsConnected; + private SimDevice m_simDevice; + private SimDouble m_simPosition; + private SimDouble m_simAbsolutePosition; + private SimDouble m_simDistancePerRotation; + private SimBoolean m_simIsConnected; /** * Construct a new DutyCycleEncoder on a specific channel. diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java index b70a5cdfb8f..0aa667ea1fe 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java @@ -29,12 +29,18 @@ * before use. */ public class Encoder implements CounterBase, Sendable, AutoCloseable { + /** Encoder indexing types. */ public enum IndexingType { + /** Reset while the signal is high. */ kResetWhileHigh(0), + /** Reset while the signal is low. */ kResetWhileLow(1), + /** Reset on falling edge of the signal. */ kResetOnFallingEdge(2), + /** Reset on rising edge of the signal. */ kResetOnRisingEdge(3); + /** IndexingType value. */ public final int value; IndexingType(int value) { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/GenericHID.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/GenericHID.java index 58e55f02425..e112e95f120 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/GenericHID.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/GenericHID.java @@ -19,32 +19,54 @@ * the mapping of ports to hardware buttons depends on the code in the Driver Station. */ public class GenericHID { - /** Represents a rumble output on the JoyStick. */ + /** Represents a rumble output on the Joystick. */ public enum RumbleType { + /** Left rumble motor. */ kLeftRumble, + /** Right rumble motor. */ kRightRumble, + /** Both left and right rumble motors. */ kBothRumble } + /** USB HID interface type. */ public enum HIDType { + /** Unknown. */ kUnknown(-1), + /** XInputUnknown. */ kXInputUnknown(0), + /** XInputGamepad. */ kXInputGamepad(1), + /** XInputWheel. */ kXInputWheel(2), + /** XInputArcadeStick. */ kXInputArcadeStick(3), + /** XInputFlightStick. */ kXInputFlightStick(4), + /** XInputDancePad. */ kXInputDancePad(5), + /** XInputGuitar. */ kXInputGuitar(6), + /** XInputGuitar2. */ kXInputGuitar2(7), + /** XInputDrumKit. */ kXInputDrumKit(8), + /** XInputGuitar3. */ kXInputGuitar3(11), + /** XInputArcadePad. */ kXInputArcadePad(19), + /** HIDJoystick. */ kHIDJoystick(20), + /** HIDGamepad. */ kHIDGamepad(21), + /** HIDDriving. */ kHIDDriving(22), + /** HIDFlight. */ kHIDFlight(23), + /** HID1stPerson. */ kHID1stPerson(24); + /** HIDType value. */ public final int value; @SuppressWarnings("PMD.UseConcurrentHashMap") @@ -60,6 +82,12 @@ public enum HIDType { } } + /** + * Creates an HIDType with the given value. + * + * @param value HIDType's value. + * @return HIDType with the given value. + */ public static HIDType of(int value) { return map.get(value); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/I2C.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/I2C.java index 0d54c4239f0..1ed131b2c70 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/I2C.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/I2C.java @@ -23,10 +23,14 @@ * WPILib Known Issues page for details. */ public class I2C implements AutoCloseable { + /** I2C connection ports. */ public enum Port { + /** Onboard I2C port. */ kOnboard(0), + /** MXP (roboRIO MXP) I2C port. */ kMXP(1); + /** Port value. */ public final int value; Port(int value) { @@ -58,10 +62,20 @@ public I2C(Port port, int deviceAddress) { HAL.report(tResourceType.kResourceType_I2C, deviceAddress); } + /** + * Returns I2C port. + * + * @return I2C port. + */ public int getPort() { return m_port; } + /** + * Returns I2C device address. + * + * @return I2C device address. + */ public int getDeviceAddress() { return m_deviceAddress; } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobotBase.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobotBase.java index bbc98a41086..12433930e5a 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobotBase.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobotBase.java @@ -5,6 +5,8 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.DriverStationJNI; +import edu.wpi.first.hal.FRCNetComm.tInstances; +import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; import edu.wpi.first.networktables.NetworkTableInstance; import edu.wpi.first.wpilibj.livewindow.LiveWindow; @@ -257,6 +259,8 @@ public void setNetworkTablesFlushEnabled(boolean enabled) { m_ntFlushEnabled = enabled; } + private boolean m_reportedLw; + /** * Sets whether LiveWindow operation is enabled during test mode. Calling * @@ -267,6 +271,10 @@ public void enableLiveWindowInTest(boolean testLW) { if (isTestEnabled()) { throw new ConcurrentModificationException("Can't configure test mode while in test mode!"); } + if (!m_reportedLw && testLW) { + HAL.report(tResourceType.kResourceType_SmartDashboard, tInstances.kSmartDashboard_LiveWindow); + m_reportedLw = true; + } m_lwEnabledInTest = testLW; } @@ -288,6 +296,7 @@ public double getPeriod() { return m_period; } + /** Loop function. */ protected void loopFunc() { DriverStation.refreshData(); m_watchdog.reset(); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Joystick.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Joystick.java index 0e112ded0f9..5536009e320 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Joystick.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Joystick.java @@ -17,20 +17,35 @@ * and the mapping of ports to hardware buttons depends on the code in the Driver Station. */ public class Joystick extends GenericHID { + /** Default X axis channel. */ public static final byte kDefaultXChannel = 0; + + /** Default Y axis channel. */ public static final byte kDefaultYChannel = 1; + + /** Default Z axis channel. */ public static final byte kDefaultZChannel = 2; + + /** Default twist axis channel. */ public static final byte kDefaultTwistChannel = 2; + + /** Default throttle axis channel. */ public static final byte kDefaultThrottleChannel = 3; /** Represents an analog axis on a joystick. */ public enum AxisType { + /** X axis. */ kX(0), + /** Y axis. */ kY(1), + /** Z axis. */ kZ(2), + /** Twist axis. */ kTwist(3), + /** Throttle axis. */ kThrottle(4); + /** AxisType value. */ public final int value; AxisType(int value) { @@ -40,9 +55,12 @@ public enum AxisType { /** Represents a digital button on a joystick. */ public enum ButtonType { + /** kTrigger. */ kTrigger(1), + /** kTop. */ kTop(2); + /** ButtonType value. */ public final int value; ButtonType(int value) { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/MotorSafety.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/MotorSafety.java index 3b1da84f2e9..dc969a9c96f 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/MotorSafety.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/MotorSafety.java @@ -186,7 +186,13 @@ public static void checkMotors() { } } + /** Called to stop the motor when the timeout expires. */ public abstract void stopMotor(); + /** + * Returns a description to print when an error occurs. + * + * @return Description to print when an error occurs. + */ public abstract String getDescription(); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS4Controller.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS4Controller.java index 3958b695f1c..e000c6f51d3 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS4Controller.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS4Controller.java @@ -34,21 +34,36 @@ public PS4Controller(int port) { /** Represents a digital button on a PS4Controller. */ public enum Button { + /** Square button. */ kSquare(1), + /** X button. */ kCross(2), + /** Circle button. */ kCircle(3), + /** Triangle button. */ kTriangle(4), + /** Left Trigger 1 button. */ kL1(5), + /** Right Trigger 1 button. */ kR1(6), + /** Left Trigger 2 button. */ kL2(7), + /** Right Trigger 2 button. */ kR2(8), + /** Share button. */ kShare(9), + /** Option button. */ kOptions(10), + /** Left stick button. */ kL3(11), + /** Right stick button. */ kR3(12), + /** PlayStation button. */ kPS(13), + /** Touchpad click button. */ kTouchpad(14); + /** Button value. */ public final int value; Button(int index) { @@ -75,13 +90,20 @@ public String toString() { /** Represents an axis on a PS4Controller. */ public enum Axis { + /** Left X axis. */ kLeftX(0), + /** Left Y axis. */ kLeftY(1), + /** Right X axis. */ kRightX(2), + /** Right Y axis. */ kRightY(5), + /** Left Trigger 2. */ kL2(3), + /** Right Trigger 2. */ kR2(4); + /** Axis value. */ public final int value; Axis(int index) { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS5Controller.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS5Controller.java index b95ce022f65..18cb738e883 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS5Controller.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PS5Controller.java @@ -31,21 +31,36 @@ public PS5Controller(int port) { /** Represents a digital button on a PS5Controller. */ public enum Button { + /** Square button. */ kSquare(1), + /** X button. */ kCross(2), + /** Circle button. */ kCircle(3), + /** Triangle button. */ kTriangle(4), + /** Left trigger 1 button. */ kL1(5), + /** Right trigger 1 button. */ kR1(6), + /** Left trigger 2 button. */ kL2(7), + /** Right trigger 2 button. */ kR2(8), + /** Create button. */ kCreate(9), + /** Options button. */ kOptions(10), + /** Left stick button. */ kL3(11), + /** Right stick button. */ kR3(12), + /** PlayStation button. */ kPS(13), + /** Touchpad click button. */ kTouchpad(14); + /** Button value. */ public final int value; Button(int index) { @@ -72,13 +87,20 @@ public String toString() { /** Represents an axis on a PS5Controller. */ public enum Axis { + /** Left X axis. */ kLeftX(0), + /** Left Y axis. */ kLeftY(1), - kL2(3), + /** Right X axis. */ kRightX(2), + /** Right Y axis. */ kRightY(5), + /** Left Trigger 2. */ + kL2(3), + /** Right Trigger 2. */ kR2(4); + /** Axis value. */ public final int value; Axis(int index) { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java index 76351560d78..af600eca0a6 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java @@ -229,6 +229,7 @@ public void setPeriodMultiplier(PeriodMultiplier mult) { } } + /** Latches PWM to zero. */ public void setZeroLatch() { PWMJNI.latchPWMZero(m_handle); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsBase.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsBase.java index d9b493b47cb..90f49f53b44 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsBase.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsBase.java @@ -4,6 +4,7 @@ package edu.wpi.first.wpilibj; +/** Interface for pneumatics devices. */ public interface PneumaticsBase extends AutoCloseable { /** * For internal use to get a module for a specific type. diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsControlModule.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsControlModule.java index aac16a37c0f..4f1d70c5161 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsControlModule.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsControlModule.java @@ -186,10 +186,23 @@ public int getSolenoidDisabledList() { return CTREPCMJNI.getSolenoidDisabledList(m_handle); } + /** + * Returns whether the solenoid is currently reporting a voltage fault. + * + * @return True if solenoid is reporting a fault, otherwise false. + * @see #getSolenoidVoltageStickyFault() + */ public boolean getSolenoidVoltageFault() { return CTREPCMJNI.getSolenoidVoltageFault(m_handle); } + /** + * Returns whether the solenoid has reported a voltage fault since sticky faults were last + * cleared. This fault is persistent and can be cleared by ClearAllStickyFaults() + * + * @return True if solenoid is reporting a fault, otherwise false. + * @see #getSolenoidVoltageFault() + */ public boolean getSolenoidVoltageStickyFault() { return CTREPCMJNI.getSolenoidVoltageStickyFault(m_handle); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsModuleType.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsModuleType.java index 215a1714ca6..e1525c39e21 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsModuleType.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsModuleType.java @@ -4,7 +4,10 @@ package edu.wpi.first.wpilibj; +/** Pneumatics module type. */ public enum PneumaticsModuleType { + /** CTRE PCM. */ CTREPCM, + /** REV PH. */ REVPH } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PowerDistribution.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PowerDistribution.java index 3a4c2ab7f21..57709b77e1c 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PowerDistribution.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PowerDistribution.java @@ -22,12 +22,17 @@ public class PowerDistribution implements Sendable, AutoCloseable { private final int m_handle; private final int m_module; + /** Default module number. */ public static final int kDefaultModule = PowerDistributionJNI.DEFAULT_MODULE; + /** Power distribution module type. */ public enum ModuleType { + /** CTRE (Cross The Road Electronics) CTRE Power Distribution Panel (PDP). */ kCTRE(PowerDistributionJNI.CTRE_TYPE), + /** REV Power Distribution Hub (PDH). */ kRev(PowerDistributionJNI.REV_TYPE); + /** ModuleType value. */ public final int value; ModuleType(int value) { @@ -184,14 +189,29 @@ public void setSwitchableChannel(boolean enabled) { PowerDistributionJNI.setSwitchableChannel(m_handle, enabled); } + /** + * Returns the power distribution version number. + * + * @return The power distribution version number. + */ public PowerDistributionVersion getVersion() { return PowerDistributionJNI.getVersion(m_handle); } + /** + * Returns the power distribution faults. + * + * @return The power distribution faults. + */ public PowerDistributionFaults getFaults() { return PowerDistributionJNI.getFaults(m_handle); } + /** + * Returns the power distribution sticky faults. + * + * @return The power distribution sticky faults. + */ public PowerDistributionStickyFaults getStickyFaults() { return PowerDistributionJNI.getStickyFaults(m_handle); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java index 087c1566026..69d0fb32e75 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java @@ -230,7 +230,7 @@ public static void initBoolean(String key, boolean value) { */ public static void setLong(String key, long value) { NetworkTableEntry entry = m_table.getEntry(key); - entry.setDouble(value); + entry.setInteger(value); entry.setPersistent(); } @@ -242,7 +242,7 @@ public static void setLong(String key, long value) { */ public static void initLong(String key, long value) { NetworkTableEntry entry = m_table.getEntry(key); - entry.setDefaultDouble(value); + entry.setDefaultInteger(value); entry.setPersistent(); } @@ -345,6 +345,6 @@ public static float getFloat(String key, float backup) { * @return either the value in the table, or the backup */ public static long getLong(String key, long backup) { - return (long) m_table.getEntry(key).getDouble(backup); + return m_table.getEntry(key).getInteger(backup); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Relay.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Relay.java index e68bf721088..dce3096cc85 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Relay.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Relay.java @@ -44,9 +44,13 @@ public InvalidValueException(String message) { /** The state to drive a Relay to. */ public enum Value { + /** Off. */ kOff("Off"), + /** On. */ kOn("On"), + /** Forward. */ kForward("Forward"), + /** Reverse. */ kReverse("Reverse"); private final String m_prettyValue; @@ -55,10 +59,21 @@ public enum Value { m_prettyValue = prettyValue; } + /** + * Returns the pretty string representation of the value. + * + * @return The pretty string representation of the value. + */ public String getPrettyValue() { return m_prettyValue; } + /** + * Returns the value for a given pretty string. + * + * @param value The pretty string. + * @return The value or an empty optional if there is no corresponding value. + */ public static Optional getValueOf(String value) { return Arrays.stream(Value.values()).filter(v -> v.m_prettyValue.equals(value)).findFirst(); } @@ -66,11 +81,11 @@ public static Optional getValueOf(String value) { /** The Direction(s) that a relay is configured to operate in. */ public enum Direction { - /** direction: both directions are valid. */ + /** Both directions are valid. */ kBoth, - /** direction: Only forward is valid. */ + /** Only forward is valid. */ kForward, - /** direction: only reverse is valid. */ + /** Only reverse is valid. */ kReverse } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java index 4d22d2a051c..89524063373 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java @@ -6,7 +6,6 @@ import edu.wpi.first.cameraserver.CameraServerShared; import edu.wpi.first.cameraserver.CameraServerSharedStore; -import edu.wpi.first.cscore.CameraServerJNI; import edu.wpi.first.hal.FRCNetComm.tInstances; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; @@ -178,6 +177,11 @@ protected RobotBase() { Shuffleboard.disableActuatorWidgets(); } + /** + * Returns the main thread ID. + * + * @return The main thread ID. + */ public static long getMainThreadId() { return m_threadId; } @@ -415,10 +419,6 @@ public static void startRobot(Supplier robotSupplier) { // Force refresh DS data DriverStation.refreshData(); - // Call a CameraServer JNI function to force OpenCV native library loading - // Needed because all the OpenCV JNI functions don't have built in loading - CameraServerJNI.enumerateSinks(); - HAL.report( tResourceType.kResourceType_Language, tInstances.kLanguage_Java, 0, WPILibVersion.Version); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java index 3e25ce6dc99..d8faafa6d37 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java @@ -79,6 +79,10 @@ public static long getFPGATime() { /** * Get the state of the "USER" button on the roboRIO. * + *

Warning: the User Button is used to stop user programs from automatically loading if it is + * held for more then 5 seconds. Because of this, it's not recommended to be used by teams for any + * other purpose. + * * @return true if the button is currently pressed down */ public static boolean getUserButton() { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotState.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotState.java index 44198df1a1f..405dfc4bc3f 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotState.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotState.java @@ -4,6 +4,7 @@ package edu.wpi.first.wpilibj; +/** Robot state utility functions. */ public final class RobotState { /** * Returns true if the robot is disabled. diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RuntimeType.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RuntimeType.java index e7af118bfdd..cad1c51b836 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RuntimeType.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RuntimeType.java @@ -6,11 +6,16 @@ import edu.wpi.first.hal.HALUtil; +/** Runtime type. */ public enum RuntimeType { + /** roboRIO 1.0. */ kRoboRIO(HALUtil.RUNTIME_ROBORIO), + /** roboRIO 2.0. */ kRoboRIO2(HALUtil.RUNTIME_ROBORIO2), + /** Simulation runtime. */ kSimulation(HALUtil.RUNTIME_SIMULATION); + /** RuntimeType value. */ public final int value; RuntimeType(int value) { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SPI.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SPI.java index 632770c80d5..1a3bb345a0f 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SPI.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SPI.java @@ -14,13 +14,20 @@ /** Represents an SPI bus port. */ public class SPI implements AutoCloseable { + /** SPI port. */ public enum Port { + /** Onboard SPI bus port CS0. */ kOnboardCS0(SPIJNI.ONBOARD_CS0_PORT), + /** Onboard SPI bus port CS1. */ kOnboardCS1(SPIJNI.ONBOARD_CS1_PORT), + /** Onboard SPI bus port CS2. */ kOnboardCS2(SPIJNI.ONBOARD_CS2_PORT), + /** Onboard SPI bus port CS3. */ kOnboardCS3(SPIJNI.ONBOARD_CS3_PORT), + /** MXP (roboRIO MXP) SPI bus port. */ kMXP(SPIJNI.MXP_PORT); + /** SPI port value. */ public final int value; Port(int value) { @@ -28,6 +35,7 @@ public enum Port { } } + /** SPI mode. */ public enum Mode { /** Clock idle low, data sampled on rising edge. */ kMode0(SPIJNI.SPI_MODE0), @@ -38,6 +46,7 @@ public enum Mode { /** Clock idle high, data sampled on rising edge. */ kMode3(SPIJNI.SPI_MODE3); + /** SPI mode value. */ public final int value; Mode(int value) { @@ -64,6 +73,11 @@ public SPI(Port port) { HAL.report(tResourceType.kResourceType_SPI, port.value + 1); } + /** + * Returns the SPI port value. + * + * @return SPI port value. + */ public int getPort() { return m_port; } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SensorUtil.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SensorUtil.java index 18ee44e9751..258fbc7c606 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SensorUtil.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SensorUtil.java @@ -47,8 +47,10 @@ public final class SensorUtil { /** Number of PCM Modules. */ public static final int kCTREPCMModules = PortsJNI.getNumCTREPCMModules(); + /** Number of power distribution channels per PH. */ public static final int kREVPHChannels = PortsJNI.getNumREVPHChannels(); + /** Number of PH modules. */ public static final int kREVPHModules = PortsJNI.getNumREVPHModules(); /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SerialPort.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SerialPort.java index c2b35cad3aa..d978d783daf 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SerialPort.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SerialPort.java @@ -13,13 +13,20 @@ public class SerialPort implements AutoCloseable { private int m_portHandle; + /** Serial port. */ public enum Port { + /** Onboard serial port on the roboRIO. */ kOnboard(0), + /** MXP (roboRIO MXP) serial port. */ kMXP(1), + /** USB serial port (same as KUSB1). */ kUSB(2), + /** USB serial port 1. */ kUSB1(2), + /** USB serial port 2. */ kUSB2(3); + /** Port value. */ public final int value; Port(int value) { @@ -29,12 +36,18 @@ public enum Port { /** Represents the parity to use for serial communications. */ public enum Parity { + /** No parity. */ kNone(0), + /** Odd parity. */ kOdd(1), + /** Even parity. */ kEven(2), + /** Parity bit always on. */ kMark(3), + /** Parity bit always off. */ kSpace(4); + /** Parity value. */ public final int value; Parity(int value) { @@ -44,10 +57,14 @@ public enum Parity { /** Represents the number of stop bits to use for Serial Communication. */ public enum StopBits { + /** One stop bit. */ kOne(10), + /** One and a half stop bits. */ kOnePointFive(15), + /** Two stop bits. */ kTwo(20); + /** StopBits value. */ public final int value; StopBits(int value) { @@ -57,11 +74,16 @@ public enum StopBits { /** Represents what type of flow control to use for serial communication. */ public enum FlowControl { + /** No flow control. */ kNone(0), + /** XON/XOFF flow control. */ kXonXoff(1), + /** RTS/CTS flow control. */ kRtsCts(2), + /** DTS/DSR flow control. */ kDtsDsr(4); + /** FlowControl value. */ public final int value; FlowControl(int value) { @@ -71,9 +93,12 @@ public enum FlowControl { /** Represents which type of buffer mode to use when writing to a serial port. */ public enum WriteBufferMode { + /** Flush the buffer on each access. */ kFlushOnAccess(1), + /** Flush the buffer when it is full. */ kFlushWhenFull(2); + /** WriteBufferMode value. */ public final int value; WriteBufferMode(int value) { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Servo.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Servo.java index a4553fbc7a0..6ba3ed7d57a 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Servo.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Servo.java @@ -19,14 +19,14 @@ public class Servo extends PWM { private static final double kMaxServoAngle = 180.0; private static final double kMinServoAngle = 0.0; - protected static final int kDefaultMaxServoPWM = 2400; - protected static final int kDefaultMinServoPWM = 600; + private static final int kDefaultMaxServoPWM = 2400; + private static final int kDefaultMinServoPWM = 600; /** - * Constructor.
+ * Constructor. * - *

By default {@value #kDefaultMaxServoPWM} ms is used as the maxPWM value
- * By default {@value #kDefaultMinServoPWM} ms is used as the minPWM value
+ *

By default, {@value #kDefaultMaxServoPWM} ms is used as the max PWM value and {@value + * #kDefaultMinServoPWM} ms is used as the minPWM value. * * @param channel The PWM channel to which the servo is attached. 0-9 are on-board, 10-19 are on * the MXP port @@ -84,7 +84,7 @@ public void setAngle(double degrees) { degrees = kMaxServoAngle; } - setPosition(((degrees - kMinServoAngle)) / getServoAngleRange()); + setPosition((degrees - kMinServoAngle) / getServoAngleRange()); } /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/StadiaController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/StadiaController.java index 6c95020dad9..d092d8b9375 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/StadiaController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/StadiaController.java @@ -19,22 +19,38 @@ public class StadiaController extends GenericHID { /** Represents a digital button on a StadiaController. */ public enum Button { + /** A button. */ kA(1), + /** B button. */ kB(2), + /** X Button. */ kX(3), + /** Y Button. */ kY(4), + /** Left bumper button. */ kLeftBumper(5), + /** Right bumper button. */ kRightBumper(6), + /** Left stick button. */ kLeftStick(7), + /** Right stick button. */ kRightStick(8), + /** Ellipses button. */ kEllipses(9), + /** Hamburger button. */ kHamburger(10), + /** Stadia button. */ kStadia(11), + /** Right trigger button. */ kRightTrigger(12), + /** Left trigger button. */ kLeftTrigger(13), + /** Google button. */ kGoogle(14), + /** Frame button. */ kFrame(15); + /** Button value. */ public final int value; Button(int value) { @@ -61,11 +77,16 @@ public String toString() { /** Represents an axis on a StadiaController. */ public enum Axis { + /** Left X axis. */ kLeftX(0), + /** Right X axis. */ kRightX(3), + /** Left Y axis. */ kLeftY(1), + /** Right Y axis. */ kRightY(4); + /** Axis value. */ public final int value; Axis(int value) { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SynchronousInterrupt.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SynchronousInterrupt.java index 705d6b1a7c7..8f7886515f7 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SynchronousInterrupt.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SynchronousInterrupt.java @@ -23,11 +23,16 @@ public class SynchronousInterrupt implements AutoCloseable { /** Event trigger combinations for a synchronous interrupt. */ public enum WaitResult { + /** Timeout event. */ kTimeout(0x0), + /** Rising edge event. */ kRisingEdge(0x1), + /** Falling edge event. */ kFallingEdge(0x100), + /** Both rising and falling edge events. */ kBoth(0x101); + /** WaitResult value. */ public final int value; WaitResult(int value) { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Threads.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Threads.java index 8b0d1670f9f..5ff0540dbfc 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Threads.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Threads.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.ThreadsJNI; +/** Thread utility functions. */ public final class Threads { /** * Get the thread priority for the current thread. diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/TimedRobot.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/TimedRobot.java index 9bce0bc6c4e..12346c445a7 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/TimedRobot.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/TimedRobot.java @@ -65,6 +65,7 @@ public int compareTo(Callback rhs) { } } + /** Default loop period. */ public static final double kDefaultPeriod = 0.02; // The C pointer to the notifier object. We don't use it directly, it is diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java index c5aab1f7877..a7b5e71b56e 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java @@ -108,6 +108,11 @@ private synchronized void initialize() { SendableRegistry.addLW(this, "Ultrasonic", m_echoChannel.getChannel()); } + /** + * Returns the echo channel. + * + * @return The echo channel. + */ public int getEchoChannel() { return m_echoChannel.getChannel(); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Watchdog.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Watchdog.java index 18d4b6463df..e1860e38db3 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Watchdog.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Watchdog.java @@ -208,6 +208,7 @@ public void suppressTimeoutMessage(boolean suppress) { m_suppressTimeoutMessage = suppress; } + @SuppressWarnings("resource") private static void updateAlarm() { if (m_watchdogs.isEmpty()) { NotifierJNI.cancelNotifierAlarm(m_notifier); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/XboxController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/XboxController.java index 6e3a0dfd545..5bd7a019274 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/XboxController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/XboxController.java @@ -23,17 +23,28 @@ public class XboxController extends GenericHID { /** Represents a digital button on an XboxController. */ public enum Button { + /** Left bumper. */ kLeftBumper(5), + /** Right bumper. */ kRightBumper(6), + /** Left stick. */ kLeftStick(9), + /** Right stick. */ kRightStick(10), + /** A. */ kA(1), + /** B. */ kB(2), + /** X. */ kX(3), + /** Y. */ kY(4), + /** Back. */ kBack(7), + /** Start. */ kStart(8); + /** Button value. */ public final int value; Button(int value) { @@ -60,13 +71,20 @@ public String toString() { /** Represents an axis on an XboxController. */ public enum Axis { + /** Left X. */ kLeftX(0), + /** Right X. */ kRightX(4), + /** Left Y. */ kLeftY(1), + /** Right Y. */ kRightY(5), + /** Left trigger. */ kLeftTrigger(2), + /** Right trigger. */ kRightTrigger(3); + /** Axis value. */ public final int value; Axis(int value) { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/EdgeConfiguration.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/EdgeConfiguration.java index 9d58fbf1db1..c969249aaf5 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/EdgeConfiguration.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/EdgeConfiguration.java @@ -4,15 +4,22 @@ package edu.wpi.first.wpilibj.counter; +/** Edge configuration. */ public enum EdgeConfiguration { + /** No edge configuration (neither rising nor falling). */ kNone(false, false), + /** Rising edge configuration. */ kRisingEdge(true, false), + /** Falling edge configuration. */ kFallingEdge(false, true), + /** Both rising and falling edge configuration. */ kBoth(true, true); + /** True if triggering on rising edge. */ @SuppressWarnings("MemberName") public final boolean rising; + /** True if triggering on falling edge. */ @SuppressWarnings("MemberName") public final boolean falling; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java index a6a59c17e52..40e34a803da 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java @@ -14,49 +14,16 @@ import edu.wpi.first.util.sendable.SendableBuilder; import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import java.util.function.DoubleConsumer; /** * A class for driving differential drive/skid-steer drive platforms such as the Kit of Parts drive * base, "tank drive", or West Coast Drive. * *

These drive bases typically have drop-center / skid-steer with two or more wheels per side - * (e.g., 6WD or 8WD). This class takes a MotorController per side. For four and six motor - * drivetrains, construct and pass in {@link - * edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup} instances as follows. - * - *

Four motor drivetrain: - * - *


- * public class Robot {
- *   MotorController m_frontLeft = new PWMVictorSPX(1);
- *   MotorController m_rearLeft = new PWMVictorSPX(2);
- *   MotorControllerGroup m_left = new MotorControllerGroup(m_frontLeft, m_rearLeft);
- *
- *   MotorController m_frontRight = new PWMVictorSPX(3);
- *   MotorController m_rearRight = new PWMVictorSPX(4);
- *   MotorControllerGroup m_right = new MotorControllerGroup(m_frontRight, m_rearRight);
- *
- *   DifferentialDrive m_drive = new DifferentialDrive(m_left, m_right);
- * }
- * 
- * - *

Six motor drivetrain: - * - *


- * public class Robot {
- *   MotorController m_frontLeft = new PWMVictorSPX(1);
- *   MotorController m_midLeft = new PWMVictorSPX(2);
- *   MotorController m_rearLeft = new PWMVictorSPX(3);
- *   MotorControllerGroup m_left = new MotorControllerGroup(m_frontLeft, m_midLeft, m_rearLeft);
- *
- *   MotorController m_frontRight = new PWMVictorSPX(4);
- *   MotorController m_midRight = new PWMVictorSPX(5);
- *   MotorController m_rearRight = new PWMVictorSPX(6);
- *   MotorControllerGroup m_right = new MotorControllerGroup(m_frontRight, m_midRight, m_rearRight);
- *
- *   DifferentialDrive m_drive = new DifferentialDrive(m_left, m_right);
- * }
- * 
+ * (e.g., 6WD or 8WD). This class takes a setter per side. For four and six motor drivetrains, use + * CAN motor controller followers or {@link + * edu.wpi.first.wpilibj.motorcontrol.PWMMotorController#addFollower(PWMMotorController)}. * *

A differential drive robot has left and right wheels separated by an arbitrary width. * @@ -88,8 +55,12 @@ public class DifferentialDrive extends RobotDriveBase implements Sendable, AutoCloseable { private static int instances; - private final MotorController m_leftMotor; - private final MotorController m_rightMotor; + private final DoubleConsumer m_leftMotor; + private final DoubleConsumer m_rightMotor; + + // Used for Sendable property getters + private double m_leftOutput; + private double m_rightOutput; private boolean m_reported; @@ -100,7 +71,10 @@ public class DifferentialDrive extends RobotDriveBase implements Sendable, AutoC */ @SuppressWarnings("MemberName") public static class WheelSpeeds { + /** Left wheel speed. */ public double left; + + /** Right wheel speed. */ public double right; /** Constructs a WheelSpeeds with zeroes for left and right speeds. */ @@ -121,22 +95,37 @@ public WheelSpeeds(double left, double right) { /** * Construct a DifferentialDrive. * - *

To pass multiple motors per side, use a {@link - * edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup}. If a motor needs to be inverted, do - * so before passing it in. + *

To pass multiple motors per side, use CAN motor controller followers or {@link + * edu.wpi.first.wpilibj.motorcontrol.PWMMotorController#addFollower(PWMMotorController)}. If a + * motor needs to be inverted, do so before passing it in. * * @param leftMotor Left motor. * @param rightMotor Right motor. */ - @SuppressWarnings("this-escape") + @SuppressWarnings({"removal", "this-escape"}) public DifferentialDrive(MotorController leftMotor, MotorController rightMotor) { + this((double output) -> leftMotor.set(output), (double output) -> rightMotor.set(output)); + SendableRegistry.addChild(this, leftMotor); + SendableRegistry.addChild(this, rightMotor); + } + + /** + * Construct a DifferentialDrive. + * + *

To pass multiple motors per side, use CAN motor controller followers or {@link + * edu.wpi.first.wpilibj.motorcontrol.PWMMotorController#addFollower(PWMMotorController)}. If a + * motor needs to be inverted, do so before passing it in. + * + * @param leftMotor Left motor setter. + * @param rightMotor Right motor setter. + */ + @SuppressWarnings("this-escape") + public DifferentialDrive(DoubleConsumer leftMotor, DoubleConsumer rightMotor) { requireNonNullParam(leftMotor, "leftMotor", "DifferentialDrive"); requireNonNullParam(rightMotor, "rightMotor", "DifferentialDrive"); m_leftMotor = leftMotor; m_rightMotor = rightMotor; - SendableRegistry.addChild(this, m_leftMotor); - SendableRegistry.addChild(this, m_rightMotor); instances++; SendableRegistry.addLW(this, "DifferentialDrive", instances); } @@ -178,8 +167,11 @@ public void arcadeDrive(double xSpeed, double zRotation, boolean squareInputs) { var speeds = arcadeDriveIK(xSpeed, zRotation, squareInputs); - m_leftMotor.set(speeds.left * m_maxOutput); - m_rightMotor.set(speeds.right * m_maxOutput); + m_leftOutput = speeds.left * m_maxOutput; + m_rightOutput = speeds.right * m_maxOutput; + + m_leftMotor.accept(m_leftOutput); + m_rightMotor.accept(m_rightOutput); feed(); } @@ -207,8 +199,11 @@ public void curvatureDrive(double xSpeed, double zRotation, boolean allowTurnInP var speeds = curvatureDriveIK(xSpeed, zRotation, allowTurnInPlace); - m_leftMotor.set(speeds.left * m_maxOutput); - m_rightMotor.set(speeds.right * m_maxOutput); + m_leftOutput = speeds.left * m_maxOutput; + m_rightOutput = speeds.right * m_maxOutput; + + m_leftMotor.accept(m_leftOutput); + m_rightMotor.accept(m_rightOutput); feed(); } @@ -245,8 +240,11 @@ public void tankDrive(double leftSpeed, double rightSpeed, boolean squareInputs) var speeds = tankDriveIK(leftSpeed, rightSpeed, squareInputs); - m_leftMotor.set(speeds.left * m_maxOutput); - m_rightMotor.set(speeds.right * m_maxOutput); + m_leftOutput = speeds.left * m_maxOutput; + m_rightOutput = speeds.right * m_maxOutput; + + m_leftMotor.accept(m_leftOutput); + m_rightMotor.accept(m_rightOutput); feed(); } @@ -351,8 +349,12 @@ public static WheelSpeeds tankDriveIK(double leftSpeed, double rightSpeed, boole @Override public void stopMotor() { - m_leftMotor.stopMotor(); - m_rightMotor.stopMotor(); + m_leftOutput = 0.0; + m_rightOutput = 0.0; + + m_leftMotor.accept(0.0); + m_rightMotor.accept(0.0); + feed(); } @@ -366,7 +368,7 @@ public void initSendable(SendableBuilder builder) { builder.setSmartDashboardType("DifferentialDrive"); builder.setActuator(true); builder.setSafeState(this::stopMotor); - builder.addDoubleProperty("Left Motor Speed", m_leftMotor::get, m_leftMotor::set); - builder.addDoubleProperty("Right Motor Speed", m_rightMotor::get, m_rightMotor::set); + builder.addDoubleProperty("Left Motor Speed", () -> m_leftOutput, m_leftMotor); + builder.addDoubleProperty("Right Motor Speed", () -> m_rightOutput, m_rightMotor); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java index abdfefedd6d..507e3719c81 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java @@ -16,6 +16,7 @@ import edu.wpi.first.util.sendable.SendableBuilder; import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import java.util.function.DoubleConsumer; /** * A class for driving Mecanum drive platforms. @@ -56,10 +57,16 @@ public class MecanumDrive extends RobotDriveBase implements Sendable, AutoCloseable { private static int instances; - private final MotorController m_frontLeftMotor; - private final MotorController m_rearLeftMotor; - private final MotorController m_frontRightMotor; - private final MotorController m_rearRightMotor; + private final DoubleConsumer m_frontLeftMotor; + private final DoubleConsumer m_rearLeftMotor; + private final DoubleConsumer m_frontRightMotor; + private final DoubleConsumer m_rearRightMotor; + + // Used for Sendable property getters + private double m_frontLeftOutput; + private double m_rearLeftOutput; + private double m_frontRightOutput; + private double m_rearRightOutput; private boolean m_reported; @@ -70,9 +77,16 @@ public class MecanumDrive extends RobotDriveBase implements Sendable, AutoClosea */ @SuppressWarnings("MemberName") public static class WheelSpeeds { + /** Front-left wheel speed. */ public double frontLeft; + + /** Front-right wheel speed. */ public double frontRight; + + /** Rear-left wheel speed. */ public double rearLeft; + + /** Rear-right wheel speed. */ public double rearRight; /** Constructs a WheelSpeeds with zeroes for all four speeds. */ @@ -104,12 +118,39 @@ public WheelSpeeds(double frontLeft, double frontRight, double rearLeft, double * @param frontRightMotor The motor on the front-right corner. * @param rearRightMotor The motor on the rear-right corner. */ - @SuppressWarnings("this-escape") + @SuppressWarnings({"removal", "this-escape"}) public MecanumDrive( MotorController frontLeftMotor, MotorController rearLeftMotor, MotorController frontRightMotor, MotorController rearRightMotor) { + this( + (double output) -> frontLeftMotor.set(output), + (double output) -> rearLeftMotor.set(output), + (double output) -> frontRightMotor.set(output), + (double output) -> rearRightMotor.set(output)); + SendableRegistry.addChild(this, frontLeftMotor); + SendableRegistry.addChild(this, rearLeftMotor); + SendableRegistry.addChild(this, frontRightMotor); + SendableRegistry.addChild(this, rearRightMotor); + } + + /** + * Construct a MecanumDrive. + * + *

If a motor needs to be inverted, do so before passing it in. + * + * @param frontLeftMotor The setter for the motor on the front-left corner. + * @param rearLeftMotor The setter for the motor on the rear-left corner. + * @param frontRightMotor The setter for the motor on the front-right corner. + * @param rearRightMotor The setter for the motor on the rear-right corner. + */ + @SuppressWarnings("this-escape") + public MecanumDrive( + DoubleConsumer frontLeftMotor, + DoubleConsumer rearLeftMotor, + DoubleConsumer frontRightMotor, + DoubleConsumer rearRightMotor) { requireNonNullParam(frontLeftMotor, "frontLeftMotor", "MecanumDrive"); requireNonNullParam(rearLeftMotor, "rearLeftMotor", "MecanumDrive"); requireNonNullParam(frontRightMotor, "frontRightMotor", "MecanumDrive"); @@ -119,10 +160,6 @@ public MecanumDrive( m_rearLeftMotor = rearLeftMotor; m_frontRightMotor = frontRightMotor; m_rearRightMotor = rearRightMotor; - SendableRegistry.addChild(this, m_frontLeftMotor); - SendableRegistry.addChild(this, m_rearLeftMotor); - SendableRegistry.addChild(this, m_frontRightMotor); - SendableRegistry.addChild(this, m_rearRightMotor); instances++; SendableRegistry.addLW(this, "MecanumDrive", instances); } @@ -172,10 +209,15 @@ public void driveCartesian(double xSpeed, double ySpeed, double zRotation, Rotat var speeds = driveCartesianIK(xSpeed, ySpeed, zRotation, gyroAngle); - m_frontLeftMotor.set(speeds.frontLeft * m_maxOutput); - m_frontRightMotor.set(speeds.frontRight * m_maxOutput); - m_rearLeftMotor.set(speeds.rearLeft * m_maxOutput); - m_rearRightMotor.set(speeds.rearRight * m_maxOutput); + m_frontLeftOutput = speeds.frontLeft * m_maxOutput; + m_rearLeftOutput = speeds.rearLeft * m_maxOutput; + m_frontRightOutput = speeds.frontRight * m_maxOutput; + m_rearRightOutput = speeds.rearRight * m_maxOutput; + + m_frontLeftMotor.accept(m_frontLeftOutput); + m_frontRightMotor.accept(m_frontRightOutput); + m_rearLeftMotor.accept(m_rearLeftOutput); + m_rearRightMotor.accept(m_rearRightOutput); feed(); } @@ -256,10 +298,16 @@ public static WheelSpeeds driveCartesianIK( @Override public void stopMotor() { - m_frontLeftMotor.stopMotor(); - m_frontRightMotor.stopMotor(); - m_rearLeftMotor.stopMotor(); - m_rearRightMotor.stopMotor(); + m_frontLeftOutput = 0.0; + m_frontRightOutput = 0.0; + m_rearLeftOutput = 0.0; + m_rearRightOutput = 0.0; + + m_frontLeftMotor.accept(0.0); + m_frontRightMotor.accept(0.0); + m_rearLeftMotor.accept(0.0); + m_rearRightMotor.accept(0.0); + feed(); } @@ -273,12 +321,10 @@ public void initSendable(SendableBuilder builder) { builder.setSmartDashboardType("MecanumDrive"); builder.setActuator(true); builder.setSafeState(this::stopMotor); + builder.addDoubleProperty("Front Left Motor Speed", () -> m_frontLeftOutput, m_frontLeftMotor); builder.addDoubleProperty( - "Front Left Motor Speed", m_frontLeftMotor::get, m_frontLeftMotor::set); - builder.addDoubleProperty( - "Front Right Motor Speed", m_frontRightMotor::get, m_frontRightMotor::set); - builder.addDoubleProperty("Rear Left Motor Speed", m_rearLeftMotor::get, m_rearLeftMotor::set); - builder.addDoubleProperty( - "Rear Right Motor Speed", m_rearRightMotor::get, m_rearRightMotor::set); + "Front Right Motor Speed", () -> m_frontRightOutput, m_frontRightMotor); + builder.addDoubleProperty("Rear Left Motor Speed", () -> m_rearLeftOutput, m_rearLeftMotor); + builder.addDoubleProperty("Rear Right Motor Speed", () -> m_rearRightOutput, m_rearRightMotor); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/RobotDriveBase.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/RobotDriveBase.java index 933773b4b82..a952b2ecaa6 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/RobotDriveBase.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/RobotDriveBase.java @@ -12,22 +12,36 @@ *

{@link edu.wpi.first.wpilibj.MotorSafety} is enabled by default. */ public abstract class RobotDriveBase extends MotorSafety { + /** Default input deadband. */ public static final double kDefaultDeadband = 0.02; + + /** Default maximum output. */ public static final double kDefaultMaxOutput = 1.0; + /** Input deadband. */ protected double m_deadband = kDefaultDeadband; + + /** Maximum output. */ protected double m_maxOutput = kDefaultMaxOutput; /** The location of a motor on the robot for the purpose of driving. */ public enum MotorType { + /** Front left motor. */ kFrontLeft(0), + /** Front right motor. */ kFrontRight(1), + /** Rear left motor. */ kRearLeft(2), + /** Reat right motor. */ kRearRight(3), + /** Left motor. */ kLeft(0), + /** Right motor. */ kRight(1), + /** Back motor. */ kBack(2); + /** MotorType value. */ public final int value; MotorType(int value) { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/EventLoop.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/EventLoop.java index 3e220e06c2d..38d21cd990a 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/EventLoop.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/EventLoop.java @@ -5,6 +5,7 @@ package edu.wpi.first.wpilibj.event; import java.util.Collection; +import java.util.ConcurrentModificationException; import java.util.LinkedHashSet; /** @@ -12,6 +13,10 @@ */ public final class EventLoop { private final Collection m_bindings = new LinkedHashSet<>(); + private boolean m_running; + + /** Default constructor. */ + public EventLoop() {} /** * Bind a new action to run when the loop is polled. @@ -19,16 +24,27 @@ public final class EventLoop { * @param action the action to run. */ public void bind(Runnable action) { + if (m_running) { + throw new ConcurrentModificationException("Cannot bind EventLoop while it is running"); + } m_bindings.add(action); } /** Poll all bindings. */ public void poll() { - m_bindings.forEach(Runnable::run); + try { + m_running = true; + m_bindings.forEach(Runnable::run); + } finally { + m_running = false; + } } /** Clear all bindings. */ public void clear() { + if (m_running) { + throw new ConcurrentModificationException("Cannot clear EventLoop while it is running"); + } m_bindings.clear(); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Accelerometer.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Accelerometer.java index d33b7ae4c13..630d45c6b79 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Accelerometer.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Accelerometer.java @@ -11,10 +11,15 @@ */ @Deprecated(since = "2024", forRemoval = true) public interface Accelerometer { + /** Accelerometer range. */ enum Range { + /** 2 Gs max. */ k2G, + /** 4 Gs max. */ k4G, + /** 8 Gs max. */ k8G, + /** 16 Gs max. */ k16G } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/internal/DriverStationModeThread.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/internal/DriverStationModeThread.java index 03d63973940..57c6bf7ddf8 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/internal/DriverStationModeThread.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/internal/DriverStationModeThread.java @@ -9,9 +9,7 @@ import edu.wpi.first.wpilibj.DriverStation; import java.util.concurrent.atomic.AtomicBoolean; -/* - * For internal use only. - */ +/** For internal use only. */ public class DriverStationModeThread implements AutoCloseable { private final AtomicBoolean m_keepAlive = new AtomicBoolean(); private final Thread m_thread; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/livewindow/LiveWindow.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/livewindow/LiveWindow.java index 018c4d2ac24..1b9d1ea24a6 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/livewindow/LiveWindow.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/livewindow/LiveWindow.java @@ -67,14 +67,29 @@ private LiveWindow() { throw new UnsupportedOperationException("This is a utility class!"); } + /** + * Sets function to be called when LiveWindow is enabled. + * + * @param runnable function (or null for none) + */ public static synchronized void setEnabledListener(Runnable runnable) { enabledListener = runnable; } + /** + * Sets function to be called when LiveWindow is disabled. + * + * @param runnable function (or null for none) + */ public static synchronized void setDisabledListener(Runnable runnable) { disabledListener = runnable; } + /** + * Returns true if LiveWindow is enabled. + * + * @return True if LiveWindow is enabled. + */ public static synchronized boolean isEnabled() { return liveWindowEnabled; } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroup.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroup.java index 88e7efe1325..c56496cd480 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroup.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroup.java @@ -9,7 +9,14 @@ import edu.wpi.first.util.sendable.SendableRegistry; import java.util.Arrays; -/** Allows multiple {@link MotorController} objects to be linked together. */ +/** + * Allows multiple {@link MotorController} objects to be linked together. + * + * @deprecated Use {@link PWMMotorController#addFollower(PWMMotorController)} or if using CAN motor + * controllers, use their method of following. + */ +@SuppressWarnings("removal") +@Deprecated(forRemoval = true, since = "2024") public class MotorControllerGroup implements MotorController, Sendable, AutoCloseable { private boolean m_isInverted; private final MotorController[] m_motorControllers; @@ -30,6 +37,11 @@ public MotorControllerGroup( init(); } + /** + * Create a new MotorControllerGroup with the provided MotorControllers. + * + * @param motorControllers The MotorControllers to add. + */ @SuppressWarnings("this-escape") public MotorControllerGroup(MotorController[] motorControllers) { m_motorControllers = Arrays.copyOf(motorControllers, motorControllers.length); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/NidecBrushless.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/NidecBrushless.java index c5aac4f090e..01af76563cb 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/NidecBrushless.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/NidecBrushless.java @@ -14,6 +14,7 @@ import edu.wpi.first.wpilibj.PWM; /** Nidec Brushless Motor. */ +@SuppressWarnings("removal") public class NidecBrushless extends MotorSafety implements MotorController, Sendable, AutoCloseable { private boolean m_isInverted; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMMotorController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMMotorController.java index 0d2f83717b3..b72cee344fe 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMMotorController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMMotorController.java @@ -9,11 +9,16 @@ import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.MotorSafety; import edu.wpi.first.wpilibj.PWM; +import java.util.ArrayList; /** Common base class for all PWM Motor Controllers. */ +@SuppressWarnings("removal") public abstract class PWMMotorController extends MotorSafety implements MotorController, Sendable, AutoCloseable { private boolean m_isInverted; + private final ArrayList m_followers = new ArrayList<>(); + + /** PWM instances for motor controller. */ protected PWM m_pwm; /** @@ -46,7 +51,15 @@ public void close() { */ @Override public void set(double speed) { - m_pwm.setSpeed(m_isInverted ? -speed : speed); + if (m_isInverted) { + speed = -speed; + } + m_pwm.setSpeed(speed); + + for (var follower : m_followers) { + follower.set(speed); + } + feed(); } @@ -117,6 +130,15 @@ public void enableDeadbandElimination(boolean eliminateDeadband) { m_pwm.enableDeadbandElimination(eliminateDeadband); } + /** + * Make the given PWM motor controller follow the output of this one. + * + * @param follower The motor controller follower. + */ + public void addFollower(PWMMotorController follower) { + m_followers.add(follower); + } + @Override public void initSendable(SendableBuilder builder) { builder.setSmartDashboardType("Motor Controller"); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/EventImportance.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/EventImportance.java index 4d64efe55dd..0d85ce69dc7 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/EventImportance.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/EventImportance.java @@ -38,6 +38,11 @@ public enum EventImportance { m_simpleName = simpleName; } + /** + * Returns name of the given enum. + * + * @return Name of the given enum. + */ public String getSimpleName() { return m_simpleName; } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/SendableCameraWrapper.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/SendableCameraWrapper.java index 150f0dbb6cb..bd428cdd00b 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/SendableCameraWrapper.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/SendableCameraWrapper.java @@ -74,7 +74,7 @@ public void close() { } } - /* + /** * Sets NetworkTable instance used for camera publisher entries. * * @param inst NetworkTable instance diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardComponent.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardComponent.java index 39078f439eb..3e1f75acd89 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardComponent.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardComponent.java @@ -26,25 +26,53 @@ public abstract class ShuffleboardComponent> private int m_width = -1; private int m_height = -1; + /** + * Constructs a ShuffleboardComponent. + * + * @param parent The parent container. + * @param title The component title. + * @param type The component type. + */ protected ShuffleboardComponent(ShuffleboardContainer parent, String title, String type) { m_parent = requireNonNullParam(parent, "parent", "ShuffleboardComponent"); m_title = requireNonNullParam(title, "title", "ShuffleboardComponent"); m_type = type; } + /** + * Constructs a ShuffleboardComponent. + * + * @param parent The parent container. + * @param title The component title. + */ protected ShuffleboardComponent(ShuffleboardContainer parent, String title) { this(parent, title, null); } + /** + * Returns the parent container. + * + * @return The parent container. + */ public final ShuffleboardContainer getParent() { return m_parent; } + /** + * Sets the component type. + * + * @param type The component type. + */ protected final void setType(String type) { m_type = type; m_metadataDirty = true; } + /** + * Returns the component type. + * + * @return The component type. + */ public final String getType() { return m_type; } @@ -109,6 +137,11 @@ public final C withSize(int width, int height) { return (C) this; } + /** + * Builds NT metadata. + * + * @param metaTable The NT metadata table. + */ protected final void buildMetadata(NetworkTable metaTable) { if (!m_metadataDirty) { return; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ADXL345Sim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ADXL345Sim.java index c50c2f8124b..bcbf3e297d6 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ADXL345Sim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ADXL345Sim.java @@ -9,10 +9,11 @@ import edu.wpi.first.wpilibj.ADXL345_SPI; import java.util.Objects; +/** Class to control a simulated ADXL345. */ public class ADXL345Sim { - protected SimDouble m_simX; - protected SimDouble m_simY; - protected SimDouble m_simZ; + private SimDouble m_simX; + private SimDouble m_simY; + private SimDouble m_simZ; /** * Constructor. @@ -48,15 +49,30 @@ private void initSim(SimDeviceSim simDevice) { Objects.requireNonNull(m_simZ); } - public void setX(double x) { - m_simX.set(x); + /** + * Sets the X acceleration. + * + * @param accel The X acceleration. + */ + public void setX(double accel) { + m_simX.set(accel); } - public void setY(double y) { - m_simY.set(y); + /** + * Sets the Y acceleration. + * + * @param accel The Y acceleration. + */ + public void setY(double accel) { + m_simY.set(accel); } - public void setZ(double z) { - m_simZ.set(z); + /** + * Sets the Z acceleration. + * + * @param accel The Z acceleration. + */ + public void setZ(double accel) { + m_simZ.set(accel); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ADXL362Sim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ADXL362Sim.java index 76df8bd6096..148f914699e 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ADXL362Sim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ADXL362Sim.java @@ -8,10 +8,11 @@ import edu.wpi.first.wpilibj.ADXL362; import java.util.Objects; +/** Class to control a simulated ADXL362. */ public class ADXL362Sim { - protected SimDouble m_simX; - protected SimDouble m_simY; - protected SimDouble m_simZ; + private SimDouble m_simX; + private SimDouble m_simY; + private SimDouble m_simZ; /** * Constructor. @@ -34,15 +35,30 @@ private void initSim(SimDeviceSim wrappedSimDevice) { Objects.requireNonNull(m_simZ); } - public void setX(double x) { - m_simX.set(x); + /** + * Sets the X acceleration. + * + * @param accel The X acceleration. + */ + public void setX(double accel) { + m_simX.set(accel); } - public void setY(double y) { - m_simY.set(y); + /** + * Sets the Y acceleration. + * + * @param accel The Y acceleration. + */ + public void setY(double accel) { + m_simY.set(accel); } - public void setZ(double z) { - m_simZ.set(z); + /** + * Sets the Z acceleration. + * + * @param accel The Z acceleration. + */ + public void setZ(double accel) { + m_simZ.set(accel); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.java index 4798ad34a4a..a1216598f25 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.java @@ -301,6 +301,13 @@ public void setPose(Pose2d pose) { m_x.set(State.kRightPosition.value, 0, 0); } + /** + * The differential drive dynamics function. + * + * @param x The state. + * @param u The input. + * @return The state derivative with respect to time. + */ protected Matrix getDynamics(Matrix x, Matrix u) { // Because G can be factored out of B, we can divide by the old ratio and multiply // by the new ratio to get a new drivetrain model. @@ -369,12 +376,18 @@ enum State { * and 16:48 8.45:1 -- 14:50 and 19:45 7.31:1 -- 14:50 and 21:43 5.95:1 -- 14:50 and 24:40 */ public enum KitbotGearing { + /** Gear ratio of 12.75:1. */ k12p75(12.75), + /** Gear ratio of 10.71:1. */ k10p71(10.71), + /** Gear ratio of 8.45:1. */ k8p45(8.45), + /** Gear ratio of 7.31:1. */ k7p31(7.31), + /** Gear ratio of 5.95:1. */ k5p95(5.95); + /** KitbotGearing value. */ public final double value; KitbotGearing(double i) { @@ -384,15 +397,24 @@ public enum KitbotGearing { /** Represents common motor layouts of the kit drivetrain. */ public enum KitbotMotor { + /** One CIM motor per drive side. */ kSingleCIMPerSide(DCMotor.getCIM(1)), + /** Two CIM motors per drive side. */ kDualCIMPerSide(DCMotor.getCIM(2)), + /** One Mini CIM motor per drive side. */ kSingleMiniCIMPerSide(DCMotor.getMiniCIM(1)), + /** Two Mini CIM motors per drive side. */ kDualMiniCIMPerSide(DCMotor.getMiniCIM(2)), + /** One Falcon 500 motor per drive side. */ kSingleFalcon500PerSide(DCMotor.getFalcon500(1)), + /** Two Falcon 500 motors per drive side. */ kDoubleFalcon500PerSide(DCMotor.getFalcon500(2)), + /** One NEO motor per drive side. */ kSingleNEOPerSide(DCMotor.getNEO(1)), + /** Two NEO motors per drive side. */ kDoubleNEOPerSide(DCMotor.getNEO(2)); + /** KitbotMotor value. */ public final DCMotor value; KitbotMotor(DCMotor i) { @@ -402,10 +424,14 @@ public enum KitbotMotor { /** Represents common wheel sizes of the kit drivetrain. */ public enum KitbotWheelSize { + /** Six inch diameter wheels. */ kSixInch(Units.inchesToMeters(6)), + /** Eight inch diameter wheels. */ kEightInch(Units.inchesToMeters(8)), + /** Ten inch diameter wheels. */ kTenInch(Units.inchesToMeters(10)); + /** KitbotWheelSize value. */ public final double value; KitbotWheelSize(double i) { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/GenericHIDSim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/GenericHIDSim.java index 67ceda766e3..9c81be88bd9 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/GenericHIDSim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/GenericHIDSim.java @@ -8,6 +8,7 @@ /** Class to control a simulated generic joystick. */ public class GenericHIDSim { + /** GenericHID port. */ protected final int m_port; /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/LinearSystemSim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/LinearSystemSim.java index 73b1170eab4..cc4db9cb2ef 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/LinearSystemSim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/LinearSystemSim.java @@ -23,21 +23,24 @@ * *

Set simulated sensor readings with the simulated positions in {@link #getOutput()} * - * @param The number of states of the system. - * @param The number of inputs to the system. - * @param The number of outputs of the system. + * @param Number of states of the system. + * @param Number of inputs to the system. + * @param Number of outputs of the system. */ public class LinearSystemSim { - // The plant that represents the linear system. + /** The plant that represents the linear system. */ protected final LinearSystem m_plant; - // Variables for state, output, and input. + /** State vector. */ protected Matrix m_x; - protected Matrix m_y; + + /** Input vector. */ protected Matrix m_u; - // The standard deviations of measurements, used for adding noise - // to the measurements. + /** Output vector. */ + protected Matrix m_y; + + /** The standard deviations of measurements, used for adding noise to the measurements. */ protected final Matrix m_measurementStdDevs; /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/PneumaticsBaseSim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/PneumaticsBaseSim.java index 156562b48a4..dde69c1891c 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/PneumaticsBaseSim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/PneumaticsBaseSim.java @@ -10,6 +10,7 @@ /** Common base class for pneumatics module simulation classes. */ public abstract class PneumaticsBaseSim { + /** PneumaticsBase index. */ protected final int m_index; /** @@ -30,10 +31,20 @@ public static PneumaticsBaseSim getForType(int module, PneumaticsModuleType type } } + /** + * Constructs a PneumaticsBaseSim with the given index. + * + * @param index The index. + */ protected PneumaticsBaseSim(int index) { m_index = index; } + /** + * Constructs a PneumaticsBaseSim for the given module. + * + * @param module The module. + */ protected PneumaticsBaseSim(PneumaticsBase module) { this(module.getModuleNumber()); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SPISim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SPISim.java index 6c0b9e99b5c..8ce955418a2 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SPISim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SPISim.java @@ -74,6 +74,13 @@ public CallbackStore registerWriteCallback(ConstBufferCallback callback) { return new CallbackStore(m_index, uid, SPIDataJNI::cancelWriteCallback); } + /** + * Register a callback to be run whenever an auto receive buffer is received. + * + * @param callback the callback + * @return the {@link CallbackStore} object associated with this callback. Save a reference to + * this object so GC doesn't cancel the callback. + */ public CallbackStore registerReadAutoReceiveBufferCallback( SpiReadAutoReceiveBufferCallback callback) { int uid = SPIDataJNI.registerReadAutoReceiveBufferCallback(m_index, callback); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SimHooks.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SimHooks.java index 1664081de98..f637b01d708 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SimHooks.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SimHooks.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.simulation.SimulatorJNI; +/** Simulation hooks. */ public final class SimHooks { private SimHooks() {} @@ -18,14 +19,21 @@ public static void setHALRuntimeType(int type) { SimulatorJNI.setRuntimeType(type); } + /** Waits until the user program has started. */ public static void waitForProgramStart() { SimulatorJNI.waitForProgramStart(); } + /** Sets that the user program has started. */ public static void setProgramStarted() { SimulatorJNI.setProgramStarted(); } + /** + * Returns true if the user program has started. + * + * @return True if the user program has started. + */ public static boolean getProgramStarted() { return SimulatorJNI.getProgramStarted(); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/UltrasonicSim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/UltrasonicSim.java index dc4ebaade19..2ad42e7afaf 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/UltrasonicSim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/UltrasonicSim.java @@ -36,14 +36,29 @@ public UltrasonicSim(@SuppressWarnings("unused") int ping, int echo) { m_simRange = simDevice.getDouble("Range (in)"); } + /** + * Sets if the range measurement is valid. + * + * @param valid True if valid + */ public void setRangeValid(boolean valid) { m_simRangeValid.set(valid); } + /** + * Sets the range measurement. + * + * @param inches The range in inches. + */ public void setRangeInches(double inches) { m_simRange.set(inches); } + /** + * Sets the range measurement. + * + * @param meters The range in meters. + */ public void setRangeMeters(double meters) { m_simRange.set(Units.metersToInches(meters)); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/MechanismObject2d.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/MechanismObject2d.java index 754bd9256cf..3dbcf5c11c3 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/MechanismObject2d.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/MechanismObject2d.java @@ -74,6 +74,11 @@ final synchronized void update(NetworkTable table) { */ protected abstract void updateEntries(NetworkTable table); + /** + * Retrieve the object's name. + * + * @return the object's name relative to its parent. + */ public final String getName() { return m_name; } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableBuilderImpl.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableBuilderImpl.java index 2c409e8314c..f15198fd102 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableBuilderImpl.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableBuilderImpl.java @@ -57,6 +57,7 @@ import java.util.function.LongSupplier; import java.util.function.Supplier; +/** Implementation detail for SendableBuilder. */ @SuppressWarnings("PMD.CompareObjectsWithEquals") public class SendableBuilderImpl implements NTSendableBuilder { @FunctionalInterface @@ -109,6 +110,9 @@ void update(boolean controllable, long time) { private final List m_closeables = new ArrayList<>(); + /** Default constructor. */ + public SendableBuilderImpl() {} + @Override @SuppressWarnings("PMD.AvoidCatchingGenericException") public void close() { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SmartDashboard.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SmartDashboard.java index d96158586af..7da170b84ea 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SmartDashboard.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SmartDashboard.java @@ -4,6 +4,7 @@ package edu.wpi.first.wpilibj.smartdashboard; +import edu.wpi.first.hal.FRCNetComm.tInstances; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; import edu.wpi.first.networktables.NetworkTable; @@ -66,7 +67,7 @@ public static synchronized void setNetworkTableInstance(NetworkTableInstance ins @SuppressWarnings("PMD.CompareObjectsWithEquals") public static synchronized void putData(String key, Sendable data) { if (!m_reported) { - HAL.report(tResourceType.kResourceType_SmartDashboard, 0); + HAL.report(tResourceType.kResourceType_SmartDashboard, tInstances.kSmartDashboard_Instance); m_reported = true; } Sendable sddata = tablesToData.get(key); @@ -120,7 +121,7 @@ public static synchronized Sendable getData(String key) { */ public static NetworkTableEntry getEntry(String key) { if (!m_reported) { - HAL.report(tResourceType.kResourceType_SmartDashboard, 0); + HAL.report(tResourceType.kResourceType_SmartDashboard, tInstances.kSmartDashboard_Instance); m_reported = true; } return table.getEntry(key); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/sysid/SysIdRoutineLog.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/sysid/SysIdRoutineLog.java new file mode 100644 index 00000000000..a8d7d940e8d --- /dev/null +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/sysid/SysIdRoutineLog.java @@ -0,0 +1,227 @@ +// 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.wpilibj.sysid; + +import static edu.wpi.first.units.Units.Amps; +import static edu.wpi.first.units.Units.Meters; +import static edu.wpi.first.units.Units.MetersPerSecond; +import static edu.wpi.first.units.Units.Rotations; +import static edu.wpi.first.units.Units.RotationsPerSecond; +import static edu.wpi.first.units.Units.Second; +import static edu.wpi.first.units.Units.Volts; + +import edu.wpi.first.units.Angle; +import edu.wpi.first.units.Current; +import edu.wpi.first.units.Distance; +import edu.wpi.first.units.Measure; +import edu.wpi.first.units.Velocity; +import edu.wpi.first.units.Voltage; +import edu.wpi.first.util.datalog.DoubleLogEntry; +import edu.wpi.first.util.datalog.StringLogEntry; +import edu.wpi.first.wpilibj.DataLogManager; +import java.util.HashMap; +import java.util.Map; + +/** + * Utility for logging data from a SysId test routine. Each complete routine (quasistatic and + * dynamic, forward and reverse) should have its own SysIdRoutineLog instance, with a unique log + * name. + */ +public class SysIdRoutineLog { + private final Map> m_logEntries = new HashMap<>(); + private final String m_logName; + private StringLogEntry m_state; + + /** + * Create a new logging utility for a SysId test routine. + * + * @param logName The name for the test routine in the log. Should be unique between complete test + * routines (quasistatic and dynamic, forward and reverse). The current state of this test + * (e.g. "quasistatic-forward") will appear in WPILog under the "sysid-test-state-logName" + * entry. + */ + public SysIdRoutineLog(String logName) { + m_logName = logName; + } + + /** Possible state of a SysId routine. */ + public enum State { + /** Quasistatic forward test. */ + kQuasistaticForward("quasistatic-forward"), + /** Quasistatic reverse test. */ + kQuasistaticReverse("quasistatic-reverse"), + /** Dynamic forward test. */ + kDynamicForward("dynamic-forward"), + /** Dynamic reverse test. */ + kDynamicReverse("dynamic-reverse"), + /** No test. */ + kNone("none"); + + private final String m_state; + + State(String state) { + m_state = state; + } + + @Override + public String toString() { + return m_state; + } + } + + /** Logs data from a single motor during a SysIdRoutine. */ + public class MotorLog { + private final String m_motorName; + + /** + * Create a new SysId motor log handle. + * + * @param motorName The name of the motor whose data is being logged. + */ + private MotorLog(String motorName) { + m_motorName = motorName; + m_logEntries.put(motorName, new HashMap<>()); + } + + /** + * Log a generic data value from the motor. + * + * @param name The name of the data field being recorded. + * @param value The numeric value of the data field. + * @param unit The unit string of the data field. + * @return The motor log (for call chaining). + */ + public MotorLog value(String name, double value, String unit) { + var motorEntries = m_logEntries.get(m_motorName); + var entry = motorEntries.get(name); + + if (entry == null) { + var log = DataLogManager.getLog(); + + entry = new DoubleLogEntry(log, name + "-" + m_motorName + "-" + m_logName, unit); + motorEntries.put(name, entry); + } + + entry.append(value); + return this; + } + + /** + * Log the voltage applied to the motor. + * + * @param voltage The voltage to record. + * @return The motor log (for call chaining). + */ + public MotorLog voltage(Measure voltage) { + return value("voltage", voltage.in(Volts), Volts.name()); + } + + /** + * Log the linear position of the motor. + * + * @param position The linear position to record. + * @return The motor log (for call chaining). + */ + public MotorLog linearPosition(Measure position) { + return value("position", position.in(Meters), Meters.name()); + } + + /** + * Log the angular position of the motor. + * + * @param position The angular position to record. + * @return The motor log (for call chaining). + */ + public MotorLog angularPosition(Measure position) { + return value("position", position.in(Rotations), Rotations.name()); + } + + /** + * Log the linear velocity of the motor. + * + * @param velocity The linear velocity to record. + * @return The motor log (for call chaining). + */ + public MotorLog linearVelocity(Measure> velocity) { + return value("velocity", velocity.in(MetersPerSecond), MetersPerSecond.name()); + } + + /** + * Log the angular velocity of the motor. + * + * @param velocity The angular velocity to record. + * @return The motor log (for call chaining). + */ + public MotorLog angularVelocity(Measure> velocity) { + return value("velocity", velocity.in(RotationsPerSecond), RotationsPerSecond.name()); + } + + /** + * Log the linear acceleration of the motor. + * + *

This is optional; SysId can perform an accurate fit without it. + * + * @param acceleration The linear acceleration to record. + * @return The motor log (for call chaining). + */ + public MotorLog linearAcceleration(Measure>> acceleration) { + return value( + "acceleration", + acceleration.in(MetersPerSecond.per(Second)), + MetersPerSecond.per(Second).name()); + } + + /** + * Log the angular acceleration of the motor. + * + *

This is optional; SysId can perform an accurate fit without it. + * + * @param acceleration The angular acceleration to record. + * @return The motor log (for call chaining). + */ + public MotorLog angularAcceleration(Measure>> acceleration) { + return value( + "acceleration", + acceleration.in(RotationsPerSecond.per(Second)), + RotationsPerSecond.per(Second).name()); + } + + /** + * Log the current applied to the motor. + * + *

This is optional; SysId can perform an accurate fit without it. + * + * @param current The current to record. + * @return The motor log (for call chaining). + */ + public MotorLog current(Measure current) { + value("current", current.in(Amps), Amps.name()); + return this; + } + } + + /** + * Log data from a motor during a SysId routine. + * + * @param motorName The name of the motor. + * @return Handle with chainable callbacks to log individual data fields. + */ + public MotorLog motor(String motorName) { + return new MotorLog(motorName); + } + + /** + * Records the current state of the SysId test routine. Should be called once per iteration during + * tests with the type of the current test, and once upon test end with state `none`. + * + * @param state The current state of the SysId test routine. + */ + public void recordState(State state) { + if (m_state == null) { + m_state = new StringLogEntry(DataLogManager.getLog(), "sysid-test-state-" + m_logName); + } + m_state.append(state.toString()); + } +} diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color.java index 47594ec1e60..50605676f75 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color.java @@ -14,9 +14,15 @@ */ @SuppressWarnings("MemberName") public class Color { + /** Red component (0-1). */ public final double red; + + /** Green component (0-1). */ public final double green; + + /** Blue component (0-1). */ public final double blue; + private String m_name; /** Constructs a default color (black). */ diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color8Bit.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color8Bit.java index 5d916e1941a..132ea64a417 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color8Bit.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color8Bit.java @@ -10,8 +10,13 @@ /** Represents colors with 8 bits of precision. */ @SuppressWarnings("MemberName") public class Color8Bit { + /** Red component (0-255). */ public final int red; + + /** Green component (0-255). */ public final int green; + + /** Blue component (0-255). */ public final int blue; /** Constructs a default color (black). */ diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/AddressableLEDBufferTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/AddressableLEDBufferTest.java index 9bf5d88aebd..6c6c145f1d9 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/AddressableLEDBufferTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/AddressableLEDBufferTest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.params.provider.Arguments.arguments; import edu.wpi.first.wpilibj.util.Color; @@ -70,4 +71,73 @@ void getColorTest() { assertEquals(firstRedColor8Bit, buffer.getLED8Bit(2)); assertEquals(firstBlueColor8Bit, buffer.getLED8Bit(3)); } + + @Test + void getRed() { + var buffer = new AddressableLEDBuffer(1); + buffer.setRGB(0, 127, 128, 129); + assertEquals(127, buffer.getRed(0)); + } + + @Test + void getGreen() { + var buffer = new AddressableLEDBuffer(1); + buffer.setRGB(0, 127, 128, 129); + assertEquals(128, buffer.getGreen(0)); + } + + @Test + void getBlue() { + var buffer = new AddressableLEDBuffer(1); + buffer.setRGB(0, 127, 128, 129); + assertEquals(129, buffer.getBlue(0)); + } + + @Test + void forEach() { + var buffer = new AddressableLEDBuffer(3); + buffer.setRGB(0, 1, 2, 3); + buffer.setRGB(1, 4, 5, 6); + buffer.setRGB(2, 7, 8, 9); + + buffer.forEach( + (index, r, g, b) -> { + switch (index) { + case 0: + { + assertAll( + () -> assertEquals(1, r, "red at index 0"), + () -> assertEquals(2, g, "green at index 0"), + () -> assertEquals(3, b, "blue at index 0")); + } + break; + case 1: + { + assertAll( + () -> assertEquals(4, r, "red at index 1"), + () -> assertEquals(5, g, "green at index 1"), + () -> assertEquals(6, b, "blue at index 1")); + } + break; + case 2: + { + assertAll( + () -> assertEquals(7, r, "red at index 2"), + () -> assertEquals(8, g, "green at index 2"), + () -> assertEquals(9, b, "blue at index 2")); + } + break; + default: + fail("Unexpected index " + index); + break; + } + }); + } + + @Test + void forEachOnEmptyBuffer() { + var buffer = new AddressableLEDBuffer(0); + + buffer.forEach((i, r, g, b) -> fail("Iterator should not be called on an empty buffer")); + } } diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/drive/DifferentialDriveTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/drive/DifferentialDriveTest.java index 4675a7f2be0..0546a4ce890 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/drive/DifferentialDriveTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/drive/DifferentialDriveTest.java @@ -6,9 +6,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.motorcontrol.MockMotorController; +import edu.wpi.first.wpilibj.motorcontrol.MockPWMMotorController; import org.junit.jupiter.api.Test; +@SuppressWarnings("resource") class DifferentialDriveTest { @Test void testArcadeDriveIK() { @@ -250,9 +251,9 @@ void testTankDriveIKSquared() { @Test void testArcadeDrive() { - var left = new MockMotorController(); - var right = new MockMotorController(); - var drive = new DifferentialDrive(left, right); + var left = new MockPWMMotorController(); + var right = new MockPWMMotorController(); + var drive = new DifferentialDrive(left::set, right::set); drive.setDeadband(0.0); // Forward @@ -288,9 +289,9 @@ void testArcadeDrive() { @Test void testArcadeDriveSquared() { - var left = new MockMotorController(); - var right = new MockMotorController(); - var drive = new DifferentialDrive(left, right); + var left = new MockPWMMotorController(); + var right = new MockPWMMotorController(); + var drive = new DifferentialDrive(left::set, right::set); drive.setDeadband(0.0); // Forward @@ -326,9 +327,9 @@ void testArcadeDriveSquared() { @Test void testCurvatureDrive() { - var left = new MockMotorController(); - var right = new MockMotorController(); - var drive = new DifferentialDrive(left, right); + var left = new MockPWMMotorController(); + var right = new MockPWMMotorController(); + var drive = new DifferentialDrive(left::set, right::set); drive.setDeadband(0.0); // Forward @@ -364,9 +365,9 @@ void testCurvatureDrive() { @Test void testCurvatureDriveTurnInPlace() { - var left = new MockMotorController(); - var right = new MockMotorController(); - var drive = new DifferentialDrive(left, right); + var left = new MockPWMMotorController(); + var right = new MockPWMMotorController(); + var drive = new DifferentialDrive(left::set, right::set); drive.setDeadband(0.0); // Forward @@ -402,9 +403,9 @@ void testCurvatureDriveTurnInPlace() { @Test void testTankDrive() { - var left = new MockMotorController(); - var right = new MockMotorController(); - var drive = new DifferentialDrive(left, right); + var left = new MockPWMMotorController(); + var right = new MockPWMMotorController(); + var drive = new DifferentialDrive(left::set, right::set); drive.setDeadband(0.0); // Forward @@ -440,9 +441,9 @@ void testTankDrive() { @Test void testTankDriveSquared() { - var left = new MockMotorController(); - var right = new MockMotorController(); - var drive = new DifferentialDrive(left, right); + var left = new MockPWMMotorController(); + var right = new MockPWMMotorController(); + var drive = new DifferentialDrive(left::set, right::set); drive.setDeadband(0.0); // Forward diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/drive/MecanumDriveTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/drive/MecanumDriveTest.java index 034ba10c0cd..8be2ccb7566 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/drive/MecanumDriveTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/drive/MecanumDriveTest.java @@ -7,9 +7,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import edu.wpi.first.math.geometry.Rotation2d; -import edu.wpi.first.wpilibj.motorcontrol.MockMotorController; +import edu.wpi.first.wpilibj.motorcontrol.MockPWMMotorController; import org.junit.jupiter.api.Test; +@SuppressWarnings("resource") class MecanumDriveTest { @Test void testCartesianIK() { @@ -89,11 +90,11 @@ void testCartesianIKGyro90CW() { @Test void testCartesian() { - var fl = new MockMotorController(); - var fr = new MockMotorController(); - var rl = new MockMotorController(); - var rr = new MockMotorController(); - var drive = new MecanumDrive(fl, rl, fr, rr); + var fl = new MockPWMMotorController(); + var rl = new MockPWMMotorController(); + var fr = new MockPWMMotorController(); + var rr = new MockPWMMotorController(); + var drive = new MecanumDrive(fl::set, rl::set, fr::set, rr::set); drive.setDeadband(0.0); // Forward @@ -134,11 +135,11 @@ void testCartesian() { @Test void testCartesianGyro90CW() { - var fl = new MockMotorController(); - var fr = new MockMotorController(); - var rl = new MockMotorController(); - var rr = new MockMotorController(); - var drive = new MecanumDrive(fl, rl, fr, rr); + var fl = new MockPWMMotorController(); + var rl = new MockPWMMotorController(); + var fr = new MockPWMMotorController(); + var rr = new MockPWMMotorController(); + var drive = new MecanumDrive(fl::set, rl::set, fr::set, rr::set); drive.setDeadband(0.0); // Forward in global frame; left in robot frame @@ -179,11 +180,11 @@ void testCartesianGyro90CW() { @Test void testPolar() { - var fl = new MockMotorController(); - var fr = new MockMotorController(); - var rl = new MockMotorController(); - var rr = new MockMotorController(); - var drive = new MecanumDrive(fl, rl, fr, rr); + var fl = new MockPWMMotorController(); + var rl = new MockPWMMotorController(); + var fr = new MockPWMMotorController(); + var rr = new MockPWMMotorController(); + var drive = new MecanumDrive(fl::set, rl::set, fr::set, rr::set); drive.setDeadband(0.0); // Forward diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/EventLoopTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/EventLoopTest.java index ea3c569e656..644c18e15e5 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/EventLoopTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/EventLoopTest.java @@ -5,7 +5,9 @@ package edu.wpi.first.wpilibj.event; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import java.util.ConcurrentModificationException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Test; @@ -58,4 +60,33 @@ void testClear() { // shouldn't change assertEquals(1, counter.get()); } + + @Test + void testConcurrentModification() { + var loop = new EventLoop(); + + loop.bind( + () -> { + assertThrows( + ConcurrentModificationException.class, + () -> { + loop.bind(() -> {}); + }); + }); + + loop.poll(); + + loop.clear(); + + loop.bind( + () -> { + assertThrows( + ConcurrentModificationException.class, + () -> { + loop.clear(); + }); + }); + + loop.poll(); + } } diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java index e7a6b8eb365..4ac3dc8f56f 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java @@ -4,6 +4,7 @@ package edu.wpi.first.wpilibj.motorcontrol; +@SuppressWarnings("removal") public class MockMotorController implements MotorController { private double m_speed; private boolean m_isInverted; diff --git a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MockPWMMotorController.java similarity index 83% rename from wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java rename to wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MockPWMMotorController.java index e7a6b8eb365..bf2c287f692 100644 --- a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MockPWMMotorController.java @@ -4,36 +4,30 @@ package edu.wpi.first.wpilibj.motorcontrol; -public class MockMotorController implements MotorController { +public class MockPWMMotorController { private double m_speed; private boolean m_isInverted; - @Override public void set(double speed) { m_speed = m_isInverted ? -speed : speed; } - @Override public double get() { return m_speed; } - @Override public void setInverted(boolean isInverted) { m_isInverted = isInverted; } - @Override public boolean getInverted() { return m_isInverted; } - @Override public void disable() { m_speed = 0; } - @Override public void stopMotor() { disable(); } diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroupTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroupTest.java index 6efb3def8b2..a8f71f6b6a9 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroupTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroupTest.java @@ -15,6 +15,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +@SuppressWarnings("removal") class MotorControllerGroupTest { private static Stream motorControllerArguments() { return IntStream.of(1, 2, 3) diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DriverStationSimTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DriverStationSimTest.java index 9a324d51538..54699e00363 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DriverStationSimTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DriverStationSimTest.java @@ -108,14 +108,18 @@ void testFmsAttached() { void testDsAttached() { HAL.initialize(500, 0); DriverStationSim.resetData(); + DriverStation.refreshData(); + assertFalse(DriverStationSim.getDsAttached()); + assertFalse(DriverStation.isDSAttached()); DriverStationSim.notifyNewData(); + assertTrue(DriverStationSim.getDsAttached()); assertTrue(DriverStation.isDSAttached()); BooleanCallback callback = new BooleanCallback(); try (CallbackStore cb = DriverStationSim.registerDsAttachedCallback(callback, false)) { DriverStationSim.setDsAttached(false); - DriverStationSim.notifyNewData(); + DriverStation.refreshData(); assertFalse(DriverStationSim.getDsAttached()); assertFalse(DriverStation.isDSAttached()); assertTrue(callback.wasTriggered()); diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java index 336489a3340..47eb9ae2592 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java @@ -22,6 +22,7 @@ class ElevatorSimTest { void testStateSpaceSimWithElevator() { RoboRioSim.resetData(); + @SuppressWarnings("resource") var controller = new PIDController(10, 0, 0); var sim = diff --git a/wpilibjExamples/build.gradle b/wpilibjExamples/build.gradle index 0d1aabde3d4..4caafc2571e 100644 --- a/wpilibjExamples/build.gradle +++ b/wpilibjExamples/build.gradle @@ -94,11 +94,8 @@ model { } } binaries.all { binary -> - lib project: ':wpilibNewCommands', library: 'wpilibNewCommands', linkage: 'shared' - lib project: ':romiVendordep', library: 'romiVendordep', linkage: 'shared' - lib project: ':xrpVendordep', library: 'xrpVendordep', linkage: 'shared' lib project: ':apriltag', library: 'apriltag', linkage: 'shared' - lib project: ':wpilibc', library: 'wpilibc', linkage: 'shared' + lib project: ':apriltag', library: 'apriltagJNIShared', linkage: 'shared' lib project: ':wpimath', library: 'wpimath', linkage: 'shared' lib project: ':wpimath', library: 'wpimathJNIShared', linkage: 'shared' project(':ntcore').addNtcoreDependency(binary, 'shared') @@ -111,7 +108,6 @@ model { lib project: ':wpiutil', library: 'wpiutilJNIShared', linkage: 'shared' lib project: ':wpinet', library: 'wpinet', linkage: 'shared' lib project: ':wpinet', library: 'wpinetJNIShared', linkage: 'shared' - lib project: ':cameraserver', library: 'cameraserver', linkage: 'shared' if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) { nativeUtils.useRequiredLibrary(binary, 'ni_link_libraries', 'ni_runtime_libraries') } else { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/apriltagsvision/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/apriltagsvision/Robot.java index 91230c28674..27ae30f32f7 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/apriltagsvision/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/apriltagsvision/Robot.java @@ -39,14 +39,14 @@ public void robotInit() { void apriltagVisionThreadProc() { var detector = new AprilTagDetector(); - // look for tag16h5, don't correct any error bits - detector.addFamily("tag16h5", 0); + // look for tag36h11, correct 3 error bits + detector.addFamily("tag36h11", 3); // Set up Pose Estimator - parameters are for a Microsoft Lifecam HD-3000 // (https://www.chiefdelphi.com/t/wpilib-apriltagdetector-sample-code/421411/21) var poseEstConfig = new AprilTagPoseEstimator.Config( - 0.1524, 699.3778103158814, 677.7161226393544, 345.6059345433618, 207.12741326228522); + 0.1651, 699.3778103158814, 677.7161226393544, 345.6059345433618, 207.12741326228522); var estimator = new AprilTagPoseEstimator(poseEstConfig); // Get the UsbCamera from CameraServer diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrive/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrive/Robot.java index 45333a5e3cf..aa343799a01 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrive/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrive/Robot.java @@ -4,6 +4,7 @@ package edu.wpi.first.wpilibj.examples.arcadedrive; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.drive.DifferentialDrive; @@ -16,9 +17,15 @@ public class Robot extends TimedRobot { private final PWMSparkMax m_leftMotor = new PWMSparkMax(0); private final PWMSparkMax m_rightMotor = new PWMSparkMax(1); - private final DifferentialDrive m_robotDrive = new DifferentialDrive(m_leftMotor, m_rightMotor); + private final DifferentialDrive m_robotDrive = + new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); private final Joystick m_stick = new Joystick(0); + public Robot() { + SendableRegistry.addChild(m_robotDrive, m_leftMotor); + SendableRegistry.addChild(m_robotDrive, m_rightMotor); + } + @Override public void robotInit() { // We need to invert one side of the drivetrain so that positive voltages diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrivexboxcontroller/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrivexboxcontroller/Robot.java index aa0f9a20ea2..a376fe0ae48 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrivexboxcontroller/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrivexboxcontroller/Robot.java @@ -4,6 +4,7 @@ package edu.wpi.first.wpilibj.examples.arcadedrivexboxcontroller; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj.drive.DifferentialDrive; @@ -16,9 +17,15 @@ public class Robot extends TimedRobot { private final PWMSparkMax m_leftMotor = new PWMSparkMax(0); private final PWMSparkMax m_rightMotor = new PWMSparkMax(1); - private final DifferentialDrive m_robotDrive = new DifferentialDrive(m_leftMotor, m_rightMotor); + private final DifferentialDrive m_robotDrive = + new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); private final XboxController m_driverController = new XboxController(0); + public Robot() { + SendableRegistry.addChild(m_robotDrive, m_leftMotor); + SendableRegistry.addChild(m_robotDrive, m_rightMotor); + } + @Override public void robotInit() { // We need to invert one side of the drivetrain so that positive voltages diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/DriveSubsystem.java index 4ecaa77231b..246b5c41185 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/DriveSubsystem.java @@ -4,28 +4,25 @@ package edu.wpi.first.wpilibj.examples.armbot.subsystems; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.armbot.Constants.DriveConstants; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final MotorControllerGroup m_leftMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kLeftMotor1Port), - new PWMSparkMax(DriveConstants.kLeftMotor2Port)); + private final PWMSparkMax m_leftLeader = new PWMSparkMax(DriveConstants.kLeftMotor1Port); + private final PWMSparkMax m_leftFollower = new PWMSparkMax(DriveConstants.kLeftMotor2Port); // The motors on the right side of the drive. - private final MotorControllerGroup m_rightMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kRightMotor1Port), - new PWMSparkMax(DriveConstants.kRightMotor2Port)); + private final PWMSparkMax m_rightLeader = new PWMSparkMax(DriveConstants.kRightMotor1Port); + private final PWMSparkMax m_rightFollower = new PWMSparkMax(DriveConstants.kRightMotor2Port); // The robot's drive - private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMotors, m_rightMotors); + private final DifferentialDrive m_drive = + new DifferentialDrive(m_leftLeader::set, m_rightLeader::set); // The left-side drive encoder private final Encoder m_leftEncoder = @@ -43,14 +40,20 @@ public class DriveSubsystem extends SubsystemBase { /** Creates a new DriveSubsystem. */ public DriveSubsystem() { + SendableRegistry.addChild(m_drive, m_leftLeader); + SendableRegistry.addChild(m_drive, m_rightLeader); + // Sets the distance per pulse for the encoders m_leftEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); m_rightEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.setInverted(true); + m_rightLeader.setInverted(true); } /** diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/ExampleSmartMotorController.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/ExampleSmartMotorController.java index 4f95b4003e6..7801a1fa8a5 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/ExampleSmartMotorController.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/ExampleSmartMotorController.java @@ -4,14 +4,12 @@ package edu.wpi.first.wpilibj.examples.armbotoffboard; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; - /** * A simplified stub class that simulates the API of a common "smart" motor controller. * *

Has no actual functionality. */ -public class ExampleSmartMotorController implements MotorController { +public class ExampleSmartMotorController { public enum PIDMode { kPosition, kVelocity, @@ -72,25 +70,19 @@ public double getEncoderRate() { /** Resets the encoder to zero distance. */ public void resetEncoder() {} - @Override public void set(double speed) {} - @Override public double get() { return 0; } - @Override public void setInverted(boolean isInverted) {} - @Override public boolean getInverted() { return false; } - @Override public void disable() {} - @Override public void stopMotor() {} } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/subsystems/DriveSubsystem.java index e3c91595370..f01ad9987d7 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/subsystems/DriveSubsystem.java @@ -4,10 +4,10 @@ package edu.wpi.first.wpilibj.examples.armbotoffboard.subsystems; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.DriveConstants; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.Commands; @@ -16,19 +16,16 @@ public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final MotorControllerGroup m_leftMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kLeftMotor1Port), - new PWMSparkMax(DriveConstants.kLeftMotor2Port)); + private final PWMSparkMax m_leftLeader = new PWMSparkMax(DriveConstants.kLeftMotor1Port); + private final PWMSparkMax m_leftFollower = new PWMSparkMax(DriveConstants.kLeftMotor2Port); // The motors on the right side of the drive. - private final MotorControllerGroup m_rightMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kRightMotor1Port), - new PWMSparkMax(DriveConstants.kRightMotor2Port)); + private final PWMSparkMax m_rightLeader = new PWMSparkMax(DriveConstants.kRightMotor1Port); + private final PWMSparkMax m_rightFollower = new PWMSparkMax(DriveConstants.kRightMotor2Port); // The robot's drive - private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMotors, m_rightMotors); + private final DifferentialDrive m_drive = + new DifferentialDrive(m_leftLeader::set, m_rightLeader::set); // The left-side drive encoder private final Encoder m_leftEncoder = @@ -46,14 +43,20 @@ public class DriveSubsystem extends SubsystemBase { /** Creates a new DriveSubsystem. */ public DriveSubsystem() { + SendableRegistry.addChild(m_drive, m_leftLeader); + SendableRegistry.addChild(m_drive, m_rightLeader); + // Sets the distance per pulse for the encoders m_leftEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); m_rightEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.setInverted(true); + m_rightLeader.setInverted(true); } /** diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdrivebot/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdrivebot/Drivetrain.java index 8b4734a0f7b..adc52e2ef67 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdrivebot/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdrivebot/Drivetrain.java @@ -12,8 +12,6 @@ import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** Represents a differential drive style drivetrain. */ @@ -25,19 +23,14 @@ public class Drivetrain { private static final double kWheelRadius = 0.0508; // meters private static final int kEncoderResolution = 4096; - private final MotorController m_leftLeader = new PWMSparkMax(1); - private final MotorController m_leftFollower = new PWMSparkMax(2); - private final MotorController m_rightLeader = new PWMSparkMax(3); - private final MotorController m_rightFollower = new PWMSparkMax(4); + private final PWMSparkMax m_leftLeader = new PWMSparkMax(1); + private final PWMSparkMax m_leftFollower = new PWMSparkMax(2); + private final PWMSparkMax m_rightLeader = new PWMSparkMax(3); + private final PWMSparkMax m_rightFollower = new PWMSparkMax(4); private final Encoder m_leftEncoder = new Encoder(0, 1); private final Encoder m_rightEncoder = new Encoder(2, 3); - private final MotorControllerGroup m_leftGroup = - new MotorControllerGroup(m_leftLeader, m_leftFollower); - private final MotorControllerGroup m_rightGroup = - new MotorControllerGroup(m_rightLeader, m_rightFollower); - private final AnalogGyro m_gyro = new AnalogGyro(0); private final PIDController m_leftPIDController = new PIDController(1, 0, 0); @@ -58,10 +51,13 @@ public class Drivetrain { public Drivetrain() { m_gyro.reset(); + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightGroup.setInverted(true); + m_rightLeader.setInverted(true); // Set the distance per pulse for the drive encoders. We can simply use the // distance traveled for one rotation of the wheel divided by the encoder @@ -90,8 +86,8 @@ public void setSpeeds(DifferentialDriveWheelSpeeds speeds) { m_leftPIDController.calculate(m_leftEncoder.getRate(), speeds.leftMetersPerSecond); final double rightOutput = m_rightPIDController.calculate(m_rightEncoder.getRate(), speeds.rightMetersPerSecond); - m_leftGroup.setVoltage(leftOutput + leftFeedforward); - m_rightGroup.setVoltage(rightOutput + rightFeedforward); + m_leftLeader.setVoltage(leftOutput + leftFeedforward); + m_rightLeader.setVoltage(rightOutput + rightFeedforward); } /** diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java index ceceaf32676..9b7ec75b5f8 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java @@ -30,8 +30,6 @@ import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.RobotController; import edu.wpi.first.wpilibj.Timer; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.simulation.AnalogGyroSim; import edu.wpi.first.wpilibj.simulation.DifferentialDrivetrainSim; @@ -48,19 +46,14 @@ public class Drivetrain { private static final double kWheelRadius = 0.0508; // meters private static final int kEncoderResolution = 4096; - private final MotorController m_leftLeader = new PWMSparkMax(1); - private final MotorController m_leftFollower = new PWMSparkMax(2); - private final MotorController m_rightLeader = new PWMSparkMax(3); - private final MotorController m_rightFollower = new PWMSparkMax(4); + private final PWMSparkMax m_leftLeader = new PWMSparkMax(1); + private final PWMSparkMax m_leftFollower = new PWMSparkMax(2); + private final PWMSparkMax m_rightLeader = new PWMSparkMax(3); + private final PWMSparkMax m_rightFollower = new PWMSparkMax(4); private final Encoder m_leftEncoder = new Encoder(0, 1); private final Encoder m_rightEncoder = new Encoder(2, 3); - private final MotorControllerGroup m_leftGroup = - new MotorControllerGroup(m_leftLeader, m_leftFollower); - private final MotorControllerGroup m_rightGroup = - new MotorControllerGroup(m_rightLeader, m_rightFollower); - private final AnalogGyro m_gyro = new AnalogGyro(0); private final PIDController m_leftPIDController = new PIDController(1, 0, 0); @@ -113,10 +106,13 @@ public class Drivetrain { public Drivetrain(DoubleArrayTopic cameraToObjectTopic) { m_gyro.reset(); + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightGroup.setInverted(true); + m_rightLeader.setInverted(true); // Set the distance per pulse for the drive encoders. We can simply use the // distance traveled for one rotation of the wheel divided by the encoder @@ -148,8 +144,8 @@ public void setSpeeds(DifferentialDriveWheelSpeeds speeds) { m_leftPIDController.calculate(m_leftEncoder.getRate(), speeds.leftMetersPerSecond); final double rightOutput = m_rightPIDController.calculate(m_rightEncoder.getRate(), speeds.rightMetersPerSecond); - m_leftGroup.setVoltage(leftOutput + leftFeedforward); - m_rightGroup.setVoltage(rightOutput + rightFeedforward); + m_leftLeader.setVoltage(leftOutput + leftFeedforward); + m_rightLeader.setVoltage(rightOutput + rightFeedforward); } /** @@ -251,8 +247,8 @@ public void simulationPeriodic() { // simulation, and write the simulated positions and velocities to our // simulated encoder and gyro. m_drivetrainSimulator.setInputs( - m_leftGroup.get() * RobotController.getInputVoltage(), - m_rightGroup.get() * RobotController.getInputVoltage()); + m_leftLeader.get() * RobotController.getInputVoltage(), + m_rightLeader.get() * RobotController.getInputVoltage()); m_drivetrainSimulator.update(0.02); m_leftEncoderSim.setDistance(m_drivetrainSimulator.getLeftPositionMeters()); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/ExampleSmartMotorController.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/ExampleSmartMotorController.java index 487974cadbc..12c51ab4414 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/ExampleSmartMotorController.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/ExampleSmartMotorController.java @@ -4,14 +4,12 @@ package edu.wpi.first.wpilibj.examples.drivedistanceoffboard; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; - /** * A simplified stub class that simulates the API of a common "smart" motor controller. * *

Has no actual functionality. */ -public class ExampleSmartMotorController implements MotorController { +public class ExampleSmartMotorController { public enum PIDMode { kPosition, kVelocity, @@ -74,27 +72,21 @@ public double getEncoderRate() { /** Resets the encoder to zero distance. */ public void resetEncoder() {} - @Override public void set(double speed) { m_value = speed; } - @Override public double get() { return m_value; } - @Override public void setInverted(boolean isInverted) {} - @Override public boolean getInverted() { return false; } - @Override public void disable() {} - @Override public void stopMotor() {} } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/subsystems/DriveSubsystem.java index 520261d4028..59d700bb1ed 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/subsystems/DriveSubsystem.java @@ -6,6 +6,7 @@ import edu.wpi.first.math.controller.SimpleMotorFeedforward; import edu.wpi.first.math.trajectory.TrapezoidProfile; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants; import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.ExampleSmartMotorController; @@ -34,20 +35,19 @@ public class DriveSubsystem extends SubsystemBase { DriveConstants.kaVoltSecondsSquaredPerMeter); // The robot's drive - private final DifferentialDrive m_drive = new DifferentialDrive(m_leftLeader, m_rightLeader); + private final DifferentialDrive m_drive = + new DifferentialDrive(m_leftLeader::set, m_rightLeader::set); /** Creates a new DriveSubsystem. */ public DriveSubsystem() { + SendableRegistry.addChild(m_drive, m_leftLeader); + SendableRegistry.addChild(m_drive, m_rightLeader); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. m_rightLeader.setInverted(true); - // You might need to not do this depending on the specific motor controller - // that you are using -- contact the respective vendor's documentation for - // more details. - m_rightFollower.setInverted(true); - m_leftFollower.follow(m_leftLeader); m_rightFollower.follow(m_rightLeader); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialprofile/ExampleSmartMotorController.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialprofile/ExampleSmartMotorController.java index e252f850039..2b37b967893 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialprofile/ExampleSmartMotorController.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorexponentialprofile/ExampleSmartMotorController.java @@ -4,14 +4,12 @@ package edu.wpi.first.wpilibj.examples.elevatorexponentialprofile; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; - /** * A simplified stub class that simulates the API of a common "smart" motor controller. * *

Has no actual functionality. */ -public class ExampleSmartMotorController implements MotorController { +public class ExampleSmartMotorController { public enum PIDMode { kPosition, kVelocity, @@ -72,25 +70,19 @@ public double getEncoderRate() { /** Resets the encoder to zero distance. */ public void resetEncoder() {} - @Override public void set(double speed) {} - @Override public double get() { return 0; } - @Override public void setInverted(boolean isInverted) {} - @Override public boolean getInverted() { return false; } - @Override public void disable() {} - @Override public void stopMotor() {} } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java index b6228c9c9e7..670fd7b8ee9 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java @@ -10,7 +10,6 @@ import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; @SuppressWarnings("PMD.RedundantFieldInitializer") @@ -27,7 +26,7 @@ public class Robot extends TimedRobot { private final Joystick m_joystick = new Joystick(1); private final Encoder m_encoder = new Encoder(1, 2); - private final MotorController m_motor = new PWMSparkMax(1); + private final PWMSparkMax m_motor = new PWMSparkMax(1); // Create a PID controller whose setpoint's change is subject to maximum // velocity and acceleration constraints. diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/ExampleSmartMotorController.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/ExampleSmartMotorController.java index a1366e6f356..6d9ac23743a 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/ExampleSmartMotorController.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/ExampleSmartMotorController.java @@ -4,14 +4,12 @@ package edu.wpi.first.wpilibj.examples.elevatortrapezoidprofile; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; - /** * A simplified stub class that simulates the API of a common "smart" motor controller. * *

Has no actual functionality. */ -public class ExampleSmartMotorController implements MotorController { +public class ExampleSmartMotorController { public enum PIDMode { kPosition, kVelocity, @@ -72,25 +70,19 @@ public double getEncoderRate() { /** Resets the encoder to zero distance. */ public void resetEncoder() {} - @Override public void set(double speed) {} - @Override public double get() { return 0; } - @Override public void setInverted(boolean isInverted) {} - @Override public boolean getInverted() { return false; } - @Override public void disable() {} - @Override public void stopMotor() {} } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java index 03c5577157a..08de65c5919 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java @@ -12,7 +12,6 @@ import edu.wpi.first.wpilibj.Ultrasonic; import edu.wpi.first.wpilibj.event.BooleanEvent; import edu.wpi.first.wpilibj.event.EventLoop; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; public class Robot extends TimedRobot { @@ -20,15 +19,15 @@ public class Robot extends TimedRobot { public static final int TOLERANCE = 8; // rpm public static final int KICKER_THRESHOLD = 15; // mm - private final MotorController m_shooter = new PWMSparkMax(0); + private final PWMSparkMax m_shooter = new PWMSparkMax(0); private final Encoder m_shooterEncoder = new Encoder(0, 1); private final PIDController m_controller = new PIDController(0.3, 0, 0); private final SimpleMotorFeedforward m_ff = new SimpleMotorFeedforward(0.1, 0.065); - private final MotorController m_kicker = new PWMSparkMax(1); + private final PWMSparkMax m_kicker = new PWMSparkMax(1); private final Ultrasonic m_kickerSensor = new Ultrasonic(2, 3); - private final MotorController m_intake = new PWMSparkMax(2); + private final PWMSparkMax m_intake = new PWMSparkMax(2); private final EventLoop m_loop = new EventLoop(); private final Joystick m_joystick = new Joystick(0); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json index 750e4dc7c22..b1ddb6c8406 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json @@ -983,5 +983,18 @@ "gradlebase": "java", "commandversion": 2, "mainclass": "Main" + }, + { + "name": "SysIdRoutine", + "description": "A sample command-based robot demonstrating use of the SysIdRoutine command factory", + "tags": [ + "SysId", + "Command-based", + "DataLog" + ], + "foldername": "sysid", + "gradlebase": "java", + "commandversion": 2, + "mainclass": "Main" } ] diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/DriveSubsystem.java index 5773da182db..dc3a3c6cc1b 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/DriveSubsystem.java @@ -4,28 +4,25 @@ package edu.wpi.first.wpilibj.examples.frisbeebot.subsystems; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.frisbeebot.Constants.DriveConstants; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final MotorControllerGroup m_leftMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kLeftMotor1Port), - new PWMSparkMax(DriveConstants.kLeftMotor2Port)); + private final PWMSparkMax m_leftLeader = new PWMSparkMax(DriveConstants.kLeftMotor1Port); + private final PWMSparkMax m_leftFollower = new PWMSparkMax(DriveConstants.kLeftMotor2Port); // The motors on the right side of the drive. - private final MotorControllerGroup m_rightMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kRightMotor1Port), - new PWMSparkMax(DriveConstants.kRightMotor2Port)); + private final PWMSparkMax m_rightLeader = new PWMSparkMax(DriveConstants.kRightMotor1Port); + private final PWMSparkMax m_rightFollower = new PWMSparkMax(DriveConstants.kRightMotor2Port); // The robot's drive - private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMotors, m_rightMotors); + private final DifferentialDrive m_drive = + new DifferentialDrive(m_leftLeader::set, m_rightLeader::set); // The left-side drive encoder private final Encoder m_leftEncoder = @@ -43,10 +40,16 @@ public class DriveSubsystem extends SubsystemBase { /** Creates a new DriveSubsystem. */ public DriveSubsystem() { + SendableRegistry.addChild(m_drive, m_leftLeader); + SendableRegistry.addChild(m_drive, m_rightLeader); + + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.setInverted(true); + m_rightLeader.setInverted(true); // Sets the distance per pulse for the encoders m_leftEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Drivetrain.java index ffde63d4026..b632f8d5cac 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Drivetrain.java @@ -4,34 +4,27 @@ package edu.wpi.first.wpilibj.examples.gearsbot.subsystems; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.AnalogInput; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.gearsbot.Constants.DriveConstants; import edu.wpi.first.wpilibj.examples.gearsbot.Robot; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class Drivetrain extends SubsystemBase { - /** - * The Drivetrain subsystem incorporates the sensors and actuators attached to the robots chassis. - * These include four drive motors, a left and right encoder and a gyro. - */ - private final MotorController m_leftMotor = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kLeftMotorPort1), - new PWMSparkMax(DriveConstants.kLeftMotorPort1)); - - private final MotorController m_rightMotor = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kRightMotorPort2), - new PWMSparkMax(DriveConstants.kLeftMotorPort2)); + // The Drivetrain subsystem incorporates the sensors and actuators attached to the robots chassis. + // These include four drive motors, a left and right encoder and a gyro. + private final PWMSparkMax m_leftLeader = new PWMSparkMax(DriveConstants.kLeftMotorPort1); + private final PWMSparkMax m_leftFollower = new PWMSparkMax(DriveConstants.kLeftMotorPort2); + private final PWMSparkMax m_rightLeader = new PWMSparkMax(DriveConstants.kRightMotorPort1); + private final PWMSparkMax m_rightFollower = new PWMSparkMax(DriveConstants.kRightMotorPort2); - private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMotor, m_rightMotor); + private final DifferentialDrive m_drive = + new DifferentialDrive(m_leftLeader::set, m_rightLeader::set); private final Encoder m_leftEncoder = new Encoder( @@ -50,10 +43,16 @@ public class Drivetrain extends SubsystemBase { public Drivetrain() { super(); + SendableRegistry.addChild(m_drive, m_leftLeader); + SendableRegistry.addChild(m_drive, m_rightLeader); + + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotor.setInverted(true); + m_rightLeader.setInverted(true); // Encoders may measure differently in the real world and in // simulation. In this example the robot moves 0.042 barleycorns diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gettingstarted/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gettingstarted/Robot.java index dd337e656a5..563235c2e1d 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gettingstarted/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gettingstarted/Robot.java @@ -4,6 +4,7 @@ package edu.wpi.first.wpilibj.examples.gettingstarted; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.XboxController; @@ -19,10 +20,16 @@ public class Robot extends TimedRobot { private final PWMSparkMax m_leftDrive = new PWMSparkMax(0); private final PWMSparkMax m_rightDrive = new PWMSparkMax(1); - private final DifferentialDrive m_robotDrive = new DifferentialDrive(m_leftDrive, m_rightDrive); + private final DifferentialDrive m_robotDrive = + new DifferentialDrive(m_leftDrive::set, m_rightDrive::set); private final XboxController m_controller = new XboxController(0); private final Timer m_timer = new Timer(); + public Robot() { + SendableRegistry.addChild(m_robotDrive, m_leftDrive); + SendableRegistry.addChild(m_robotDrive, m_rightDrive); + } + /** * This function is run when the robot is first started up and should be used for any * initialization code. diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyro/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyro/Robot.java index 1f9e41df523..7d00c274e57 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyro/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyro/Robot.java @@ -4,6 +4,7 @@ package edu.wpi.first.wpilibj.examples.gyro; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; @@ -30,10 +31,16 @@ public class Robot extends TimedRobot { private final PWMSparkMax m_leftDrive = new PWMSparkMax(kLeftMotorPort); private final PWMSparkMax m_rightDrive = new PWMSparkMax(kRightMotorPort); - private final DifferentialDrive m_myRobot = new DifferentialDrive(m_leftDrive, m_rightDrive); + private final DifferentialDrive m_robotDrive = + new DifferentialDrive(m_leftDrive::set, m_rightDrive::set); private final AnalogGyro m_gyro = new AnalogGyro(kGyroPort); private final Joystick m_joystick = new Joystick(kJoystickPort); + public Robot() { + SendableRegistry.addChild(m_robotDrive, m_leftDrive); + SendableRegistry.addChild(m_robotDrive, m_rightDrive); + } + @Override public void robotInit() { m_gyro.setSensitivity(kVoltsPerDegreePerSecond); @@ -50,6 +57,6 @@ public void robotInit() { @Override public void teleopPeriodic() { double turningValue = (kAngleSetpoint - m_gyro.getAngle()) * kP; - m_myRobot.arcadeDrive(-m_joystick.getY(), -turningValue); + m_robotDrive.arcadeDrive(-m_joystick.getY(), -turningValue); } } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/subsystems/DriveSubsystem.java index 9adb6ec0f96..db874104b6b 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/subsystems/DriveSubsystem.java @@ -4,29 +4,26 @@ package edu.wpi.first.wpilibj.examples.gyrodrivecommands.subsystems; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.ADXRS450_Gyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.gyrodrivecommands.Constants.DriveConstants; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final MotorControllerGroup m_leftMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kLeftMotor1Port), - new PWMSparkMax(DriveConstants.kLeftMotor2Port)); + private final PWMSparkMax m_leftLeader = new PWMSparkMax(DriveConstants.kLeftMotor1Port); + private final PWMSparkMax m_leftFollower = new PWMSparkMax(DriveConstants.kLeftMotor2Port); // The motors on the right side of the drive. - private final MotorControllerGroup m_rightMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kRightMotor1Port), - new PWMSparkMax(DriveConstants.kRightMotor2Port)); + private final PWMSparkMax m_rightLeader = new PWMSparkMax(DriveConstants.kRightMotor1Port); + private final PWMSparkMax m_rightFollower = new PWMSparkMax(DriveConstants.kRightMotor2Port); // The robot's drive - private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMotors, m_rightMotors); + private final DifferentialDrive m_drive = + new DifferentialDrive(m_leftLeader::set, m_rightLeader::set); // The left-side drive encoder private final Encoder m_leftEncoder = @@ -47,10 +44,16 @@ public class DriveSubsystem extends SubsystemBase { /** Creates a new DriveSubsystem. */ public DriveSubsystem() { + SendableRegistry.addChild(m_drive, m_leftLeader); + SendableRegistry.addChild(m_drive, m_rightLeader); + + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.setInverted(true); + m_rightLeader.setInverted(true); // Sets the distance per pulse for the encoders m_leftEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyromecanum/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyromecanum/Robot.java index b86277b8a88..a43523252c4 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyromecanum/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyromecanum/Robot.java @@ -4,6 +4,7 @@ package edu.wpi.first.wpilibj.examples.gyromecanum; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; @@ -37,12 +38,17 @@ public void robotInit() { PWMSparkMax frontRight = new PWMSparkMax(kFrontRightChannel); PWMSparkMax rearRight = new PWMSparkMax(kRearRightChannel); + SendableRegistry.addChild(m_robotDrive, frontLeft); + SendableRegistry.addChild(m_robotDrive, rearLeft); + SendableRegistry.addChild(m_robotDrive, frontRight); + SendableRegistry.addChild(m_robotDrive, rearRight); + // Invert the right side motors. // You may need to change or remove this to match your robot. frontRight.setInverted(true); rearRight.setInverted(true); - m_robotDrive = new MecanumDrive(frontLeft, rearLeft, frontRight, rearRight); + m_robotDrive = new MecanumDrive(frontLeft::set, rearLeft::set, frontRight::set, rearRight::set); m_gyro.setSensitivity(kVoltsPerDegreePerSecond); } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/DriveSubsystem.java index 1376195dc15..8de4a90c294 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/DriveSubsystem.java @@ -5,28 +5,25 @@ package edu.wpi.first.wpilibj.examples.hatchbotinlined.subsystems; import edu.wpi.first.util.sendable.SendableBuilder; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.hatchbotinlined.Constants.DriveConstants; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final MotorControllerGroup m_leftMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kLeftMotor1Port), - new PWMSparkMax(DriveConstants.kLeftMotor2Port)); + private final PWMSparkMax m_leftLeader = new PWMSparkMax(DriveConstants.kLeftMotor1Port); + private final PWMSparkMax m_leftFollower = new PWMSparkMax(DriveConstants.kLeftMotor2Port); // The motors on the right side of the drive. - private final MotorControllerGroup m_rightMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kRightMotor1Port), - new PWMSparkMax(DriveConstants.kRightMotor2Port)); + private final PWMSparkMax m_rightLeader = new PWMSparkMax(DriveConstants.kRightMotor1Port); + private final PWMSparkMax m_rightFollower = new PWMSparkMax(DriveConstants.kRightMotor2Port); // The robot's drive - private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMotors, m_rightMotors); + private final DifferentialDrive m_drive = + new DifferentialDrive(m_leftLeader::set, m_rightLeader::set); // The left-side drive encoder private final Encoder m_leftEncoder = @@ -44,10 +41,16 @@ public class DriveSubsystem extends SubsystemBase { /** Creates a new DriveSubsystem. */ public DriveSubsystem() { + SendableRegistry.addChild(m_drive, m_leftLeader); + SendableRegistry.addChild(m_drive, m_rightLeader); + + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.setInverted(true); + m_rightLeader.setInverted(true); // Sets the distance per pulse for the encoders m_leftEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/DriveSubsystem.java index 8a5296d8604..8942b41ca97 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/DriveSubsystem.java @@ -5,28 +5,25 @@ package edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems; import edu.wpi.first.util.sendable.SendableBuilder; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.hatchbottraditional.Constants.DriveConstants; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final MotorControllerGroup m_leftMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kLeftMotor1Port), - new PWMSparkMax(DriveConstants.kLeftMotor2Port)); + private final PWMSparkMax m_leftLeader = new PWMSparkMax(DriveConstants.kLeftMotor1Port); + private final PWMSparkMax m_leftFollower = new PWMSparkMax(DriveConstants.kLeftMotor2Port); // The motors on the right side of the drive. - private final MotorControllerGroup m_rightMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kRightMotor1Port), - new PWMSparkMax(DriveConstants.kRightMotor2Port)); + private final PWMSparkMax m_rightLeader = new PWMSparkMax(DriveConstants.kRightMotor1Port); + private final PWMSparkMax m_rightFollower = new PWMSparkMax(DriveConstants.kRightMotor2Port); // The robot's drive - private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMotors, m_rightMotors); + private final DifferentialDrive m_drive = + new DifferentialDrive(m_leftLeader::set, m_rightLeader::set); // The left-side drive encoder private final Encoder m_leftEncoder = @@ -44,10 +41,16 @@ public class DriveSubsystem extends SubsystemBase { /** Creates a new DriveSubsystem. */ public DriveSubsystem() { + SendableRegistry.addChild(m_drive, m_leftLeader); + SendableRegistry.addChild(m_drive, m_rightLeader); + + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.setInverted(true); + m_rightLeader.setInverted(true); // Sets the distance per pulse for the encoders m_leftEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java index 15e813a0c30..c64c9b3958a 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java @@ -14,7 +14,6 @@ import edu.wpi.first.math.kinematics.MecanumDriveWheelSpeeds; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** Represents a mecanum drive style drivetrain. */ @@ -22,10 +21,10 @@ public class Drivetrain { public static final double kMaxSpeed = 3.0; // 3 meters per second public static final double kMaxAngularSpeed = Math.PI; // 1/2 rotation per second - private final MotorController m_frontLeftMotor = new PWMSparkMax(1); - private final MotorController m_frontRightMotor = new PWMSparkMax(2); - private final MotorController m_backLeftMotor = new PWMSparkMax(3); - private final MotorController m_backRightMotor = new PWMSparkMax(4); + private final PWMSparkMax m_frontLeftMotor = new PWMSparkMax(1); + private final PWMSparkMax m_frontRightMotor = new PWMSparkMax(2); + private final PWMSparkMax m_backLeftMotor = new PWMSparkMax(3); + private final PWMSparkMax m_backRightMotor = new PWMSparkMax(4); private final Encoder m_frontLeftEncoder = new Encoder(0, 1); private final Encoder m_frontRightEncoder = new Encoder(2, 3); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java index 6d731bad428..b3f9b3462ab 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java @@ -9,6 +9,7 @@ import edu.wpi.first.math.kinematics.MecanumDriveOdometry; import edu.wpi.first.math.kinematics.MecanumDriveWheelPositions; import edu.wpi.first.math.kinematics.MecanumDriveWheelSpeeds; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.ADXRS450_Gyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.MecanumDrive; @@ -23,7 +24,7 @@ public class DriveSubsystem extends SubsystemBase { private final PWMSparkMax m_rearRight = new PWMSparkMax(DriveConstants.kRearRightMotorPort); private final MecanumDrive m_drive = - new MecanumDrive(m_frontLeft, m_rearLeft, m_frontRight, m_rearRight); + new MecanumDrive(m_frontLeft::set, m_rearLeft::set, m_frontRight::set, m_rearRight::set); // The front-left-side drive encoder private final Encoder m_frontLeftEncoder = @@ -65,6 +66,11 @@ public class DriveSubsystem extends SubsystemBase { /** Creates a new DriveSubsystem. */ public DriveSubsystem() { + SendableRegistry.addChild(m_drive, m_frontLeft); + SendableRegistry.addChild(m_drive, m_rearLeft); + SendableRegistry.addChild(m_drive, m_frontRight); + SendableRegistry.addChild(m_drive, m_rearRight); + // Sets the distance per pulse for the encoders m_frontLeftEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); m_rearLeftEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java index f37654321da..77aa0cedb71 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java @@ -4,6 +4,7 @@ package edu.wpi.first.wpilibj.examples.mecanumdrive; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.drive.MecanumDrive; @@ -28,12 +29,17 @@ public void robotInit() { PWMSparkMax frontRight = new PWMSparkMax(kFrontRightChannel); PWMSparkMax rearRight = new PWMSparkMax(kRearRightChannel); + SendableRegistry.addChild(m_robotDrive, frontLeft); + SendableRegistry.addChild(m_robotDrive, rearLeft); + SendableRegistry.addChild(m_robotDrive, frontRight); + SendableRegistry.addChild(m_robotDrive, rearRight); + // Invert the right side motors. // You may need to change or remove this to match your robot. frontRight.setInverted(true); rearRight.setInverted(true); - m_robotDrive = new MecanumDrive(frontLeft, rearLeft, frontRight, rearRight); + m_robotDrive = new MecanumDrive(frontLeft::set, rearLeft::set, frontRight::set, rearRight::set); m_stick = new Joystick(kJoystickChannel); } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java index b5c34c47bf6..0625b3fc374 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java @@ -18,7 +18,6 @@ import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Timer; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** Represents a mecanum drive style drivetrain. */ @@ -26,10 +25,10 @@ public class Drivetrain { public static final double kMaxSpeed = 3.0; // 3 meters per second public static final double kMaxAngularSpeed = Math.PI; // 1/2 rotation per second - private final MotorController m_frontLeftMotor = new PWMSparkMax(1); - private final MotorController m_frontRightMotor = new PWMSparkMax(2); - private final MotorController m_backLeftMotor = new PWMSparkMax(3); - private final MotorController m_backRightMotor = new PWMSparkMax(4); + private final PWMSparkMax m_frontLeftMotor = new PWMSparkMax(1); + private final PWMSparkMax m_frontRightMotor = new PWMSparkMax(2); + private final PWMSparkMax m_backLeftMotor = new PWMSparkMax(3); + private final PWMSparkMax m_backRightMotor = new PWMSparkMax(4); private final Encoder m_frontLeftEncoder = new Encoder(0, 1); private final Encoder m_frontRightEncoder = new Encoder(2, 3); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/motorcontrol/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/motorcontrol/Robot.java index 82ced20919a..cbb4babbdf0 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/motorcontrol/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/motorcontrol/Robot.java @@ -7,7 +7,6 @@ import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; @@ -27,7 +26,7 @@ public class Robot extends TimedRobot { private static final int kEncoderPortA = 0; private static final int kEncoderPortB = 1; - private MotorController m_motor; + private PWMSparkMax m_motor; private Joystick m_joystick; private Encoder m_encoder; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java index 288baeda4b4..a59c139a538 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java @@ -7,29 +7,26 @@ import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.math.kinematics.DifferentialDriveOdometry; import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.ADXRS450_Gyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.ramsetecommand.Constants.DriveConstants; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final MotorControllerGroup m_leftMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kLeftMotor1Port), - new PWMSparkMax(DriveConstants.kLeftMotor2Port)); + private final PWMSparkMax m_leftLeader = new PWMSparkMax(DriveConstants.kLeftMotor1Port); + private final PWMSparkMax m_leftFollower = new PWMSparkMax(DriveConstants.kLeftMotor2Port); // The motors on the right side of the drive. - private final MotorControllerGroup m_rightMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kRightMotor1Port), - new PWMSparkMax(DriveConstants.kRightMotor2Port)); + private final PWMSparkMax m_rightLeader = new PWMSparkMax(DriveConstants.kRightMotor1Port); + private final PWMSparkMax m_rightFollower = new PWMSparkMax(DriveConstants.kRightMotor2Port); // The robot's drive - private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMotors, m_rightMotors); + private final DifferentialDrive m_drive = + new DifferentialDrive(m_leftLeader::set, m_rightLeader::set); // The left-side drive encoder private final Encoder m_leftEncoder = @@ -53,10 +50,16 @@ public class DriveSubsystem extends SubsystemBase { /** Creates a new DriveSubsystem. */ public DriveSubsystem() { + SendableRegistry.addChild(m_drive, m_leftLeader); + SendableRegistry.addChild(m_drive, m_rightLeader); + + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.setInverted(true); + m_rightLeader.setInverted(true); // Sets the distance per pulse for the encoders m_leftEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); @@ -99,7 +102,6 @@ public DifferentialDriveWheelSpeeds getWheelSpeeds() { * @param pose The pose to which to set the odometry. */ public void resetOdometry(Pose2d pose) { - resetEncoders(); m_odometry.resetPosition( m_gyro.getRotation2d(), m_leftEncoder.getDistance(), m_rightEncoder.getDistance(), pose); } @@ -121,8 +123,8 @@ public void arcadeDrive(double fwd, double rot) { * @param rightVolts the commanded right output */ public void tankDriveVolts(double leftVolts, double rightVolts) { - m_leftMotors.setVoltage(leftVolts); - m_rightMotors.setVoltage(rightVolts); + m_leftLeader.setVoltage(leftVolts); + m_rightLeader.setVoltage(rightVolts); m_drive.feed(); } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Drivetrain.java index 69288a15a99..98d185694e0 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Drivetrain.java @@ -13,8 +13,6 @@ import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** Represents a differential drive style drivetrain. */ @@ -26,19 +24,14 @@ public class Drivetrain { private static final double kWheelRadius = 0.0508; // meters private static final int kEncoderResolution = 4096; - private final MotorController m_leftLeader = new PWMSparkMax(1); - private final MotorController m_leftFollower = new PWMSparkMax(2); - private final MotorController m_rightLeader = new PWMSparkMax(3); - private final MotorController m_rightFollower = new PWMSparkMax(4); + private final PWMSparkMax m_leftLeader = new PWMSparkMax(1); + private final PWMSparkMax m_leftFollower = new PWMSparkMax(2); + private final PWMSparkMax m_rightLeader = new PWMSparkMax(3); + private final PWMSparkMax m_rightFollower = new PWMSparkMax(4); private final Encoder m_leftEncoder = new Encoder(0, 1); private final Encoder m_rightEncoder = new Encoder(2, 3); - private final MotorControllerGroup m_leftGroup = - new MotorControllerGroup(m_leftLeader, m_leftFollower); - private final MotorControllerGroup m_rightGroup = - new MotorControllerGroup(m_rightLeader, m_rightFollower); - private final AnalogGyro m_gyro = new AnalogGyro(0); private final PIDController m_leftPIDController = new PIDController(1, 0, 0); @@ -59,10 +52,13 @@ public class Drivetrain { public Drivetrain() { m_gyro.reset(); + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightGroup.setInverted(true); + m_rightLeader.setInverted(true); // Set the distance per pulse for the drive encoders. We can simply use the // distance traveled for one rotation of the wheel divided by the encoder @@ -91,8 +87,8 @@ public void setSpeeds(DifferentialDriveWheelSpeeds speeds) { m_leftPIDController.calculate(m_leftEncoder.getRate(), speeds.leftMetersPerSecond); final double rightOutput = m_rightPIDController.calculate(m_rightEncoder.getRate(), speeds.rightMetersPerSecond); - m_leftGroup.setVoltage(leftOutput + leftFeedforward); - m_rightGroup.setVoltage(rightOutput + rightFeedforward); + m_leftLeader.setVoltage(leftOutput + leftFeedforward); + m_rightLeader.setVoltage(rightOutput + rightFeedforward); } /** diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/RapidReactCommandBot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/RapidReactCommandBot.java index 12d7f662c6a..88b1913aa09 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/RapidReactCommandBot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/RapidReactCommandBot.java @@ -46,11 +46,12 @@ public class RapidReactCommandBot { */ public void configureBindings() { // Automatically run the storage motor whenever the ball storage is not full, - // and turn it off whenever it fills. - new Trigger(m_storage::isFull).whileFalse(m_storage.runCommand()); + // and turn it off whenever it fills. Uses subsystem-hosted trigger to + // improve readability and make inter-subsystem communication easier. + m_storage.hasCargo.whileFalse(m_storage.runCommand()); // Automatically disable and retract the intake whenever the ball storage is full. - new Trigger(m_storage::isFull).onTrue(m_intake.retractCommand()); + m_storage.hasCargo.onTrue(m_intake.retractCommand()); // Control the drive with split-stick arcade controls m_drive.setDefaultCommand( diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Drive.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Drive.java index 736ff446da4..dd7854d15ff 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Drive.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Drive.java @@ -4,10 +4,10 @@ package edu.wpi.first.wpilibj.examples.rapidreactcommandbot.subsystems; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.Constants.DriveConstants; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.SubsystemBase; @@ -15,19 +15,16 @@ public class Drive extends SubsystemBase { // The motors on the left side of the drive. - private final MotorControllerGroup m_leftMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kLeftMotor1Port), - new PWMSparkMax(DriveConstants.kLeftMotor2Port)); + private final PWMSparkMax m_leftLeader = new PWMSparkMax(DriveConstants.kLeftMotor1Port); + private final PWMSparkMax m_leftFollower = new PWMSparkMax(DriveConstants.kLeftMotor2Port); // The motors on the right side of the drive. - private final MotorControllerGroup m_rightMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kRightMotor1Port), - new PWMSparkMax(DriveConstants.kRightMotor2Port)); + private final PWMSparkMax m_rightLeader = new PWMSparkMax(DriveConstants.kRightMotor1Port); + private final PWMSparkMax m_rightFollower = new PWMSparkMax(DriveConstants.kRightMotor2Port); // The robot's drive - private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMotors, m_rightMotors); + private final DifferentialDrive m_drive = + new DifferentialDrive(m_leftLeader::set, m_rightLeader::set); // The left-side drive encoder private final Encoder m_leftEncoder = @@ -45,10 +42,16 @@ public class Drive extends SubsystemBase { /** Creates a new Drive subsystem. */ public Drive() { + SendableRegistry.addChild(m_drive, m_leftLeader); + SendableRegistry.addChild(m_drive, m_rightLeader); + + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.setInverted(true); + m_rightLeader.setInverted(true); // Sets the distance per pulse for the encoders m_leftEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Storage.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Storage.java index dac61a43fce..f3812b1d894 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Storage.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Storage.java @@ -4,28 +4,29 @@ package edu.wpi.first.wpilibj.examples.rapidreactcommandbot.subsystems; -import static edu.wpi.first.wpilibj.examples.rapidreactcommandbot.Constants.StorageConstants; - import edu.wpi.first.wpilibj.DigitalInput; +import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.Constants.StorageConstants; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.SubsystemBase; +import edu.wpi.first.wpilibj2.command.button.Trigger; public class Storage extends SubsystemBase { private final PWMSparkMax m_motor = new PWMSparkMax(StorageConstants.kMotorPort); private final DigitalInput m_ballSensor = new DigitalInput(StorageConstants.kBallSensorPort); + // Expose trigger from subsystem to improve readability and ease + // inter-subsystem communications + /** Whether the ball storage is full. */ + @SuppressWarnings("checkstyle:MemberName") + public final Trigger hasCargo = new Trigger(m_ballSensor::get); + /** Create a new Storage subsystem. */ public Storage() { // Set default command to turn off the storage motor and then idle setDefaultCommand(runOnce(m_motor::disable).andThen(run(() -> {})).withName("Idle")); } - /** Whether the ball storage is full. */ - public boolean isFull() { - return m_ballSensor.get(); - } - /** Returns a command that runs the storage motor indefinitely. */ public Command runCommand() { return run(() -> m_motor.set(1)).withName("run"); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/Drivetrain.java index d805590c068..42cf45eef8c 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/Drivetrain.java @@ -4,6 +4,7 @@ package edu.wpi.first.wpilibj.examples.romireference.subsystems; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.BuiltInAccelerometer; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.DifferentialDrive; @@ -26,7 +27,8 @@ public class Drivetrain extends SubsystemBase { private final Encoder m_rightEncoder = new Encoder(6, 7); // Set up the differential drive controller - private final DifferentialDrive m_diffDrive = new DifferentialDrive(m_leftMotor, m_rightMotor); + private final DifferentialDrive m_diffDrive = + new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); // Set up the RomiGyro private final RomiGyro m_gyro = new RomiGyro(); @@ -36,6 +38,9 @@ public class Drivetrain extends SubsystemBase { /** Creates a new Drivetrain. */ public Drivetrain() { + SendableRegistry.addChild(m_diffDrive, m_leftMotor); + SendableRegistry.addChild(m_diffDrive, m_rightMotor); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/shuffleboard/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/shuffleboard/Robot.java index 47109e0f2fb..436d72685d4 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/shuffleboard/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/shuffleboard/Robot.java @@ -5,6 +5,7 @@ package edu.wpi.first.wpilibj.examples.shuffleboard; import edu.wpi.first.networktables.GenericEntry; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.AnalogPotentiometer; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.TimedRobot; @@ -16,8 +17,10 @@ import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardTab; public class Robot extends TimedRobot { + private final PWMSparkMax m_leftDriveMotor = new PWMSparkMax(0); + private final PWMSparkMax m_rightDriveMotor = new PWMSparkMax(1); private final DifferentialDrive m_tankDrive = - new DifferentialDrive(new PWMSparkMax(0), new PWMSparkMax(1)); + new DifferentialDrive(m_leftDriveMotor::set, m_rightDriveMotor::set); private final Encoder m_leftEncoder = new Encoder(0, 1); private final Encoder m_rightEncoder = new Encoder(2, 3); @@ -27,6 +30,9 @@ public class Robot extends TimedRobot { @Override public void robotInit() { + SendableRegistry.addChild(m_tankDrive, m_leftDriveMotor); + SendableRegistry.addChild(m_tankDrive, m_rightDriveMotor); + // Add a 'max speed' widget to a tab named 'Configuration', using a number slider // The widget will be placed in the second column and row and will be TWO columns wide m_maxSpeed = diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Drivetrain.java index a9f53a9170d..a69839bc7e7 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Drivetrain.java @@ -18,7 +18,6 @@ import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.RobotController; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.simulation.AnalogGyroSim; import edu.wpi.first.wpilibj.simulation.DifferentialDrivetrainSim; @@ -41,11 +40,6 @@ public class Drivetrain { private final PWMSparkMax m_rightLeader = new PWMSparkMax(3); private final PWMSparkMax m_rightFollower = new PWMSparkMax(4); - private final MotorControllerGroup m_leftGroup = - new MotorControllerGroup(m_leftLeader, m_leftFollower); - private final MotorControllerGroup m_rightGroup = - new MotorControllerGroup(m_rightLeader, m_rightFollower); - private final Encoder m_leftEncoder = new Encoder(0, 1); private final Encoder m_rightEncoder = new Encoder(2, 3); @@ -77,10 +71,13 @@ public class Drivetrain { /** Subsystem constructor. */ public Drivetrain() { + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightGroup.setInverted(true); + m_rightLeader.setInverted(true); // Set the distance per pulse for the drive encoders. We can simply use the // distance traveled for one rotation of the wheel divided by the encoder @@ -91,7 +88,7 @@ public Drivetrain() { m_leftEncoder.reset(); m_rightEncoder.reset(); - m_rightGroup.setInverted(true); + m_rightLeader.setInverted(true); SmartDashboard.putData("Field", m_fieldSim); } @@ -104,8 +101,8 @@ public void setSpeeds(DifferentialDriveWheelSpeeds speeds) { double rightOutput = m_rightPIDController.calculate(m_rightEncoder.getRate(), speeds.rightMetersPerSecond); - m_leftGroup.setVoltage(leftOutput + leftFeedforward); - m_rightGroup.setVoltage(rightOutput + rightFeedforward); + m_leftLeader.setVoltage(leftOutput + leftFeedforward); + m_rightLeader.setVoltage(rightOutput + rightFeedforward); } /** @@ -126,8 +123,6 @@ public void updateOdometry() { /** Resets robot odometry. */ public void resetOdometry(Pose2d pose) { - m_leftEncoder.reset(); - m_rightEncoder.reset(); m_drivetrainSimulator.setPose(pose); m_odometry.resetPosition( m_gyro.getRotation2d(), m_leftEncoder.getDistance(), m_rightEncoder.getDistance(), pose); @@ -145,8 +140,8 @@ public void simulationPeriodic() { // simulated encoder and gyro. We negate the right side so that positive // voltages make the right side move forward. m_drivetrainSimulator.setInputs( - m_leftGroup.get() * RobotController.getInputVoltage(), - m_rightGroup.get() * RobotController.getInputVoltage()); + m_leftLeader.get() * RobotController.getInputVoltage(), + m_rightLeader.get() * RobotController.getInputVoltage()); m_drivetrainSimulator.update(0.02); m_leftEncoderSim.setDistance(m_drivetrainSimulator.getLeftPositionMeters()); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java index 1b24a612501..3f91c93ab21 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java @@ -19,7 +19,6 @@ import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** @@ -92,7 +91,7 @@ public class Robot extends TimedRobot { // An encoder set up to measure arm position in radians. private final Encoder m_encoder = new Encoder(kEncoderAChannel, kEncoderBChannel); - private final MotorController m_motor = new PWMSparkMax(kMotorPort); + private final PWMSparkMax m_motor = new PWMSparkMax(kMotorPort); // A joystick to read the trigger from. private final Joystick m_joystick = new Joystick(kJoystickPort); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java index 406178a8abd..01360f2fb2e 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java @@ -9,13 +9,13 @@ import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.math.kinematics.DifferentialDriveOdometry; import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.ADXRS450_Gyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.RobotBase; import edu.wpi.first.wpilibj.RobotController; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.statespacedifferentialdrivesimulation.Constants.DriveConstants; -import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.simulation.ADXRS450_GyroSim; import edu.wpi.first.wpilibj.simulation.DifferentialDrivetrainSim; @@ -26,19 +26,16 @@ public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final MotorControllerGroup m_leftMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kLeftMotor1Port), - new PWMSparkMax(DriveConstants.kLeftMotor2Port)); + private final PWMSparkMax m_leftLeader = new PWMSparkMax(DriveConstants.kLeftMotor1Port); + private final PWMSparkMax m_leftFollower = new PWMSparkMax(DriveConstants.kLeftMotor2Port); // The motors on the right side of the drive. - private final MotorControllerGroup m_rightMotors = - new MotorControllerGroup( - new PWMSparkMax(DriveConstants.kRightMotor1Port), - new PWMSparkMax(DriveConstants.kRightMotor2Port)); + private final PWMSparkMax m_rightLeader = new PWMSparkMax(DriveConstants.kRightMotor1Port); + private final PWMSparkMax m_rightFollower = new PWMSparkMax(DriveConstants.kRightMotor2Port); // The robot's drive - private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMotors, m_rightMotors); + private final DifferentialDrive m_drive = + new DifferentialDrive(m_leftLeader::set, m_rightLeader::set); // The left-side drive encoder private final Encoder m_leftEncoder = @@ -70,10 +67,16 @@ public class DriveSubsystem extends SubsystemBase { /** Creates a new DriveSubsystem. */ public DriveSubsystem() { + SendableRegistry.addChild(m_drive, m_leftLeader); + SendableRegistry.addChild(m_drive, m_rightLeader); + + m_leftLeader.addFollower(m_leftFollower); + m_rightLeader.addFollower(m_rightFollower); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. - m_rightMotors.setInverted(true); + m_rightLeader.setInverted(true); // Sets the distance per pulse for the encoders m_leftEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); @@ -131,8 +134,8 @@ public void simulationPeriodic() { // We negate the right side so that positive voltages make the right side // move forward. m_drivetrainSimulator.setInputs( - m_leftMotors.get() * RobotController.getBatteryVoltage(), - m_rightMotors.get() * RobotController.getBatteryVoltage()); + m_leftLeader.get() * RobotController.getBatteryVoltage(), + m_rightLeader.get() * RobotController.getBatteryVoltage()); m_drivetrainSimulator.update(0.020); m_leftEncoderSim.setDistance(m_drivetrainSimulator.getLeftPositionMeters()); @@ -176,7 +179,6 @@ public DifferentialDriveWheelSpeeds getWheelSpeeds() { * @param pose The pose to which to set the odometry. */ public void resetOdometry(Pose2d pose) { - resetEncoders(); m_drivetrainSimulator.setPose(pose); m_odometry.resetPosition( Rotation2d.fromDegrees(getHeading()), @@ -202,8 +204,8 @@ public void arcadeDrive(double fwd, double rot) { * @param rightVolts the commanded right output */ public void tankDriveVolts(double leftVolts, double rightVolts) { - m_leftMotors.setVoltage(leftVolts); - m_rightMotors.setVoltage(rightVolts); + m_leftLeader.setVoltage(leftVolts); + m_rightLeader.setVoltage(rightVolts); m_drive.feed(); } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java index cb196c814cd..52941913d1b 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java @@ -19,7 +19,6 @@ import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** @@ -96,7 +95,7 @@ public class Robot extends TimedRobot { // An encoder set up to measure elevator height in meters. private final Encoder m_encoder = new Encoder(kEncoderAChannel, kEncoderBChannel); - private final MotorController m_motor = new PWMSparkMax(kMotorPort); + private final PWMSparkMax m_motor = new PWMSparkMax(kMotorPort); // A joystick to read the trigger from. private final Joystick m_joystick = new Joystick(kJoystickPort); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java index 06e8d29bb66..94566eac348 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java @@ -17,7 +17,6 @@ import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** @@ -77,7 +76,7 @@ public class Robot extends TimedRobot { // An encoder set up to measure flywheel velocity in radians per second. private final Encoder m_encoder = new Encoder(kEncoderAChannel, kEncoderBChannel); - private final MotorController m_motor = new PWMSparkMax(kMotorPort); + private final PWMSparkMax m_motor = new PWMSparkMax(kMotorPort); // A joystick to read the trigger from. private final Joystick m_joystick = new Joystick(kJoystickPort); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java index 26ee8f702ac..3a073fe9d5e 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java @@ -16,7 +16,6 @@ import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** @@ -72,7 +71,7 @@ public class Robot extends TimedRobot { // An encoder set up to measure flywheel velocity in radians per second. private final Encoder m_encoder = new Encoder(kEncoderAChannel, kEncoderBChannel); - private final MotorController m_motor = new PWMSparkMax(kMotorPort); + private final PWMSparkMax m_motor = new PWMSparkMax(kMotorPort); // A joystick to read the trigger from. private final Joystick m_joystick = new Joystick(kJoystickPort); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java index 65089f973da..c168860c99b 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java @@ -12,7 +12,6 @@ import edu.wpi.first.math.kinematics.SwerveModuleState; import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; public class SwerveModule { @@ -23,8 +22,8 @@ public class SwerveModule { private static final double kModuleMaxAngularAcceleration = 2 * Math.PI; // radians per second squared - private final MotorController m_driveMotor; - private final MotorController m_turningMotor; + private final PWMSparkMax m_driveMotor; + private final PWMSparkMax m_turningMotor; private final Encoder m_driveEncoder; private final Encoder m_turningEncoder; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/SwerveModule.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/SwerveModule.java index 938b6c8f3c7..8ee8f4b07b8 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/SwerveModule.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervedriveposeestimator/SwerveModule.java @@ -12,7 +12,6 @@ import edu.wpi.first.math.kinematics.SwerveModuleState; import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; public class SwerveModule { @@ -23,8 +22,8 @@ public class SwerveModule { private static final double kModuleMaxAngularAcceleration = 2 * Math.PI; // radians per second squared - private final MotorController m_driveMotor; - private final MotorController m_turningMotor; + private final PWMSparkMax m_driveMotor; + private final PWMSparkMax m_turningMotor; private final Encoder m_driveEncoder; private final Encoder m_turningEncoder; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/Constants.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/Constants.java new file mode 100644 index 00000000000..285b98f4070 --- /dev/null +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/Constants.java @@ -0,0 +1,83 @@ +// 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.wpilibj.examples.sysid; + +import edu.wpi.first.math.util.Units; + +/** + * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean + * constants. This class should not be used for any other purpose. All constants should be declared + * globally (i.e. public static). Do not put anything functional in this class. + * + *

It is advised to statically import this class (or one of its inner classes) wherever the + * constants are needed, to reduce verbosity. + */ +public final class Constants { + public static final class DriveConstants { + public static final int kLeftMotor1Port = 0; + public static final int kLeftMotor2Port = 1; + public static final int kRightMotor1Port = 2; + public static final int kRightMotor2Port = 3; + + public static final int[] kLeftEncoderPorts = {0, 1}; + public static final int[] kRightEncoderPorts = {2, 3}; + public static final boolean kLeftEncoderReversed = false; + public static final boolean kRightEncoderReversed = true; + + public static final int kEncoderCPR = 1024; + public static final double kWheelDiameterMeters = Units.inchesToMeters(6); + public static final double kEncoderDistancePerPulse = + // Assumes the encoders are directly mounted on the wheel shafts + (kWheelDiameterMeters * Math.PI) / (double) kEncoderCPR; + } + + public static final class ShooterConstants { + public static final int[] kEncoderPorts = {4, 5}; + public static final boolean kEncoderReversed = false; + public static final int kEncoderCPR = 1024; + public static final double kEncoderDistancePerPulse = + // Distance units will be rotations + 1.0 / (double) kEncoderCPR; + + public static final int kShooterMotorPort = 4; + public static final int kFeederMotorPort = 5; + + public static final double kShooterFreeRPS = 5300; + public static final double kShooterTargetRPS = 4000; + public static final double kShooterToleranceRPS = 50; + + // These are not real PID gains, and will have to be tuned for your specific robot. + public static final double kP = 1; + + // On a real robot the feedforward constants should be empirically determined; these are + // reasonable guesses. + public static final double kSVolts = 0.05; + public static final double kVVoltSecondsPerRotation = + // Should have value 12V at free speed... + 12.0 / kShooterFreeRPS; + + public static final double kFeederSpeed = 0.5; + } + + public static final class IntakeConstants { + public static final int kMotorPort = 6; + public static final int[] kSolenoidPorts = {2, 3}; + } + + public static final class StorageConstants { + public static final int kMotorPort = 7; + public static final int kBallSensorPort = 6; + } + + public static final class AutoConstants { + public static final double kTimeoutSeconds = 3; + public static final double kDriveDistanceMeters = 2; + public static final double kDriveSpeed = 0.5; + } + + public static final class OIConstants { + public static final int kDriverControllerPort = 0; + } +} diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/Main.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/Main.java new file mode 100644 index 00000000000..1fdd4b7374c --- /dev/null +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/Main.java @@ -0,0 +1,25 @@ +// 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.wpilibj.examples.sysid; + +import edu.wpi.first.wpilibj.RobotBase; + +/** + * Do NOT add any static variables to this class, or any initialization at all. Unless you know what + * you are doing, do not modify this file except to change the parameter class to the startRobot + * call. + */ +public final class Main { + private Main() {} + + /** + * Main initialization function. Do not perform any initialization here. + * + *

If you change your main robot class, change the parameter type. + */ + public static void main(String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/Robot.java new file mode 100644 index 00000000000..0b0f1428913 --- /dev/null +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/Robot.java @@ -0,0 +1,92 @@ +// 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.wpilibj.examples.sysid; + +import edu.wpi.first.wpilibj.TimedRobot; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; + +/** + * The VM is configured to automatically run this class, and to call the functions corresponding to + * each mode, as described in the TimedRobot documentation. If you change the name of this class or + * the package after creating this project, you must also update the build.gradle file in the + * project. + */ +public class Robot extends TimedRobot { + private Command m_autonomousCommand; + + private final SysIdRoutineBot m_robot = new SysIdRoutineBot(); + + /** + * This function is run when the robot is first started up and should be used for any + * initialization code. + */ + @Override + public void robotInit() { + // Configure default commands and condition bindings on robot startup + m_robot.configureBindings(); + } + + /** + * This function is called every robot packet, no matter the mode. Use this for items like + * diagnostics that you want ran during disabled, autonomous, teleoperated and test. + * + *

This runs after the mode specific periodic functions, but before LiveWindow and + * SmartDashboard integrated updating. + */ + @Override + public void robotPeriodic() { + // Runs the Scheduler. This is responsible for polling buttons, adding newly-scheduled + // commands, running already-scheduled commands, removing finished or interrupted commands, + // and running subsystem periodic() methods. This must be called from the robot's periodic + // block in order for anything in the Command-based framework to work. + CommandScheduler.getInstance().run(); + } + + /** This function is called once each time the robot enters Disabled mode. */ + @Override + public void disabledInit() {} + + @Override + public void disabledPeriodic() {} + + @Override + public void autonomousInit() { + m_autonomousCommand = m_robot.getAutonomousCommand(); + + if (m_autonomousCommand != null) { + m_autonomousCommand.schedule(); + } + } + + /** This function is called periodically during autonomous. */ + @Override + public void autonomousPeriodic() {} + + @Override + public void teleopInit() { + // This makes sure that the autonomous stops running when + // teleop starts running. If you want the autonomous to + // continue until interrupted by another command, remove + // this line or comment it out. + if (m_autonomousCommand != null) { + m_autonomousCommand.cancel(); + } + } + + /** This function is called periodically during operator control. */ + @Override + public void teleopPeriodic() {} + + @Override + public void testInit() { + // Cancels all running commands at the start of test mode. + CommandScheduler.getInstance().cancelAll(); + } + + /** This function is called periodically during test mode. */ + @Override + public void testPeriodic() {} +} diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/SysIdRoutineBot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/SysIdRoutineBot.java new file mode 100644 index 00000000000..6665f3f7cb0 --- /dev/null +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/SysIdRoutineBot.java @@ -0,0 +1,60 @@ +// 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.wpilibj.examples.sysid; + +import static edu.wpi.first.wpilibj.examples.sysid.Constants.OIConstants; + +import edu.wpi.first.wpilibj.examples.sysid.subsystems.Drive; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import edu.wpi.first.wpilibj2.command.button.Trigger; +import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine; + +/** + * This class is where the bulk of the robot should be declared. Since Command-based is a + * "declarative" paradigm, very little robot logic should actually be handled in the {@link Robot} + * periodic methods (other than the scheduler calls). Instead, the structure of the robot (including + * subsystems, commands, and button mappings) should be declared here. + */ +public class SysIdRoutineBot { + // The robot's subsystems + private final Drive m_drive = new Drive(); + + // The driver's controller + CommandXboxController m_driverController = + new CommandXboxController(OIConstants.kDriverControllerPort); + + /** + * Use this method to define bindings between conditions and commands. These are useful for + * automating robot behaviors based on button and sensor input. + * + *

Should be called during {@link Robot#robotInit()}. + * + *

Event binding methods are available on the {@link Trigger} class. + */ + public void configureBindings() { + // Control the drive with split-stick arcade controls + m_drive.setDefaultCommand( + m_drive.arcadeDriveCommand( + () -> -m_driverController.getLeftY(), () -> -m_driverController.getRightX())); + + // Bind full set of SysId routine tests to buttons; a complete routine should run each of these + // once. + m_driverController.a().whileTrue(m_drive.sysIdQuasistatic(SysIdRoutine.Direction.kForward)); + m_driverController.b().whileTrue(m_drive.sysIdQuasistatic(SysIdRoutine.Direction.kReverse)); + m_driverController.x().whileTrue(m_drive.sysIdDynamic(SysIdRoutine.Direction.kForward)); + m_driverController.y().whileTrue(m_drive.sysIdDynamic(SysIdRoutine.Direction.kReverse)); + } + + /** + * Use this to define the command that runs during autonomous. + * + *

Scheduled during {@link Robot#autonomousInit()}. + */ + public Command getAutonomousCommand() { + // Do nothing + return m_drive.run(() -> {}); + } +} diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/subsystems/Drive.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/subsystems/Drive.java new file mode 100644 index 00000000000..3c9610897c5 --- /dev/null +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid/subsystems/Drive.java @@ -0,0 +1,132 @@ +// 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.wpilibj.examples.sysid.subsystems; + +import static edu.wpi.first.units.MutableMeasure.mutable; +import static edu.wpi.first.units.Units.Meters; +import static edu.wpi.first.units.Units.MetersPerSecond; +import static edu.wpi.first.units.Units.Volts; + +import edu.wpi.first.units.Distance; +import edu.wpi.first.units.Measure; +import edu.wpi.first.units.MutableMeasure; +import edu.wpi.first.units.Velocity; +import edu.wpi.first.units.Voltage; +import edu.wpi.first.wpilibj.Encoder; +import edu.wpi.first.wpilibj.RobotController; +import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.Constants.DriveConstants; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine; +import java.util.function.DoubleSupplier; + +public class Drive extends SubsystemBase { + // The motors on the left side of the drive. + private final PWMSparkMax m_leftMotor = new PWMSparkMax(DriveConstants.kLeftMotor1Port); + + // The motors on the right side of the drive. + private final PWMSparkMax m_rightMotor = new PWMSparkMax(DriveConstants.kRightMotor1Port); + + // The robot's drive + private final DifferentialDrive m_drive = + new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); + + // The left-side drive encoder + private final Encoder m_leftEncoder = + new Encoder( + DriveConstants.kLeftEncoderPorts[0], + DriveConstants.kLeftEncoderPorts[1], + DriveConstants.kLeftEncoderReversed); + + // The right-side drive encoder + private final Encoder m_rightEncoder = + new Encoder( + DriveConstants.kRightEncoderPorts[0], + DriveConstants.kRightEncoderPorts[1], + DriveConstants.kRightEncoderReversed); + + // Mutable holder for unit-safe voltage values, persisted to avoid reallocation. + private final MutableMeasure m_appliedVoltage = mutable(Volts.of(0)); + // Mutable holder for unit-safe linear distance values, persisted to avoid reallocation. + private final MutableMeasure m_distance = mutable(Meters.of(0)); + // Mutable holder for unit-safe linear velocity values, persisted to avoid reallocation. + private final MutableMeasure> m_velocity = mutable(MetersPerSecond.of(0)); + + // Create a new SysId routine for characterizing the drive. + private final SysIdRoutine m_sysIdRoutine = + new SysIdRoutine( + // Empty config defaults to 1 volt/second ramp rate and 7 volt step voltage. + new SysIdRoutine.Config(), + new SysIdRoutine.Mechanism( + // Tell SysId how to plumb the driving voltage to the motors. + (Measure volts) -> { + m_leftMotor.setVoltage(volts.in(Volts)); + m_rightMotor.setVoltage(volts.in(Volts)); + }, + // Tell SysId how to record a frame of data for each motor on the mechanism being + // characterized. + log -> { + // Record a frame for the left motors. Since these share an encoder, we consider + // the entire group to be one motor. + log.motor("drive-left") + .voltage( + m_appliedVoltage.mut_replace( + m_leftMotor.get() * RobotController.getBatteryVoltage(), Volts)) + .linearPosition(m_distance.mut_replace(m_leftEncoder.getDistance(), Meters)) + .linearVelocity( + m_velocity.mut_replace(m_leftEncoder.getRate(), MetersPerSecond)); + // Record a frame for the right motors. Since these share an encoder, we consider + // the entire group to be one motor. + log.motor("drive-right") + .voltage( + m_appliedVoltage.mut_replace( + m_rightMotor.get() * RobotController.getBatteryVoltage(), Volts)) + .linearPosition(m_distance.mut_replace(m_rightEncoder.getDistance(), Meters)) + .linearVelocity( + m_velocity.mut_replace(m_rightEncoder.getRate(), MetersPerSecond)); + }, + // Tell SysId to make generated commands require this subsystem, suffix test state in + // WPILog with this subsystem's name ("drive") + this)); + + /** Creates a new Drive subsystem. */ + public Drive() { + // Add the second motors on each side of the drivetrain + m_leftMotor.addFollower(new PWMSparkMax(DriveConstants.kLeftMotor2Port)); + m_rightMotor.addFollower(new PWMSparkMax(DriveConstants.kRightMotor2Port)); + + // We need to invert one side of the drivetrain so that positive voltages + // result in both sides moving forward. Depending on how your robot's + // gearbox is constructed, you might have to invert the left side instead. + m_rightMotor.setInverted(true); + + // Sets the distance per pulse for the encoders + m_leftEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); + m_rightEncoder.setDistancePerPulse(DriveConstants.kEncoderDistancePerPulse); + } + + /** + * Returns a command that drives the robot with arcade controls. + * + * @param fwd the commanded forward movement + * @param rot the commanded rotation + */ + public Command arcadeDriveCommand(DoubleSupplier fwd, DoubleSupplier rot) { + // A split-stick arcade command, with forward/backward controlled by the left + // hand, and turning controlled by the right. + return run(() -> m_drive.arcadeDrive(fwd.getAsDouble(), rot.getAsDouble())) + .withName("arcadeDrive"); + } + + public Command sysIdQuasistatic(SysIdRoutine.Direction direction) { + return m_sysIdRoutine.quasistatic(direction); + } + + public Command sysIdDynamic(SysIdRoutine.Direction direction) { + return m_sysIdRoutine.dynamic(direction); + } +} diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrive/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrive/Robot.java index 3883d145866..a217f51b559 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrive/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrive/Robot.java @@ -4,10 +4,10 @@ package edu.wpi.first.wpilibj.examples.tankdrive; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.drive.DifferentialDrive; -import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** @@ -15,27 +15,30 @@ * the code necessary to operate a robot with tank drive. */ public class Robot extends TimedRobot { - private DifferentialDrive m_myRobot; + private DifferentialDrive m_robotDrive; private Joystick m_leftStick; private Joystick m_rightStick; - private final MotorController m_leftMotor = new PWMSparkMax(0); - private final MotorController m_rightMotor = new PWMSparkMax(1); + private final PWMSparkMax m_leftMotor = new PWMSparkMax(0); + private final PWMSparkMax m_rightMotor = new PWMSparkMax(1); @Override public void robotInit() { + SendableRegistry.addChild(m_robotDrive, m_leftMotor); + SendableRegistry.addChild(m_robotDrive, m_rightMotor); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. m_rightMotor.setInverted(true); - m_myRobot = new DifferentialDrive(m_leftMotor, m_rightMotor); + m_robotDrive = new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); m_leftStick = new Joystick(0); m_rightStick = new Joystick(1); } @Override public void teleopPeriodic() { - m_myRobot.tankDrive(-m_leftStick.getY(), -m_rightStick.getY()); + m_robotDrive.tankDrive(-m_leftStick.getY(), -m_rightStick.getY()); } } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrivexboxcontroller/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrivexboxcontroller/Robot.java index c6a985af5c9..c1a850c5f70 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrivexboxcontroller/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrivexboxcontroller/Robot.java @@ -4,6 +4,7 @@ package edu.wpi.first.wpilibj.examples.tankdrivexboxcontroller; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj.drive.DifferentialDrive; @@ -16,11 +17,15 @@ public class Robot extends TimedRobot { private final PWMSparkMax m_leftMotor = new PWMSparkMax(0); private final PWMSparkMax m_rightMotor = new PWMSparkMax(1); - private final DifferentialDrive m_robotDrive = new DifferentialDrive(m_leftMotor, m_rightMotor); + private final DifferentialDrive m_robotDrive = + new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); private final XboxController m_driverController = new XboxController(0); @Override public void robotInit() { + SendableRegistry.addChild(m_robotDrive, m_leftMotor); + SendableRegistry.addChild(m_robotDrive, m_rightMotor); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java index 8c22e423f04..33681d00859 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java @@ -6,6 +6,7 @@ import edu.wpi.first.math.controller.PIDController; import edu.wpi.first.math.filter.MedianFilter; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.Ultrasonic; import edu.wpi.first.wpilibj.drive.DifferentialDrive; @@ -40,9 +41,15 @@ public class Robot extends TimedRobot { private final Ultrasonic m_ultrasonic = new Ultrasonic(kUltrasonicPingPort, kUltrasonicEchoPort); private final PWMSparkMax m_leftMotor = new PWMSparkMax(kLeftMotorPort); private final PWMSparkMax m_rightMotor = new PWMSparkMax(kRightMotorPort); - private final DifferentialDrive m_robotDrive = new DifferentialDrive(m_leftMotor, m_rightMotor); + private final DifferentialDrive m_robotDrive = + new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); private final PIDController m_pidController = new PIDController(kP, kI, kD); + public Robot() { + SendableRegistry.addChild(m_robotDrive, m_leftMotor); + SendableRegistry.addChild(m_robotDrive, m_rightMotor); + } + @Override public void autonomousInit() { // Set setpoint of the pid controller diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/subsystems/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/subsystems/Drivetrain.java index f618add665d..87b985d6522 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/subsystems/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/xrpreference/subsystems/Drivetrain.java @@ -4,6 +4,7 @@ package edu.wpi.first.wpilibj.examples.xrpreference.subsystems; +import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.BuiltInAccelerometer; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.DifferentialDrive; @@ -29,7 +30,8 @@ public class Drivetrain extends SubsystemBase { private final Encoder m_rightEncoder = new Encoder(6, 7); // Set up the differential drive controller - private final DifferentialDrive m_diffDrive = new DifferentialDrive(m_leftMotor, m_rightMotor); + private final DifferentialDrive m_diffDrive = + new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); // Set up the XRPGyro private final XRPGyro m_gyro = new XRPGyro(); @@ -39,6 +41,9 @@ public class Drivetrain extends SubsystemBase { /** Creates a new Drivetrain. */ public Drivetrain() { + SendableRegistry.addChild(m_diffDrive, m_leftMotor); + SendableRegistry.addChild(m_diffDrive, m_rightMotor); + // We need to invert one side of the drivetrain so that positive voltages // result in both sides moving forward. Depending on how your robot's // gearbox is constructed, you might have to invert the left side instead. diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romicommandbased/subsystems/RomiDrivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romicommandbased/subsystems/RomiDrivetrain.java index 276e5cee7d8..c1cfac1f325 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romicommandbased/subsystems/RomiDrivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romicommandbased/subsystems/RomiDrivetrain.java @@ -24,7 +24,8 @@ public class RomiDrivetrain extends SubsystemBase { private final Encoder m_rightEncoder = new Encoder(6, 7); // Set up the differential drive controller - private final DifferentialDrive m_diffDrive = new DifferentialDrive(m_leftMotor, m_rightMotor); + private final DifferentialDrive m_diffDrive = + new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); /** Creates a new RomiDrivetrain. */ public RomiDrivetrain() { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romieducational/RomiDrivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romieducational/RomiDrivetrain.java index 4b73a448a08..7d2a4381cb2 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romieducational/RomiDrivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romieducational/RomiDrivetrain.java @@ -23,7 +23,8 @@ public class RomiDrivetrain { private final Encoder m_rightEncoder = new Encoder(6, 7); // Set up the differential drive controller - private final DifferentialDrive m_diffDrive = new DifferentialDrive(m_leftMotor, m_rightMotor); + private final DifferentialDrive m_diffDrive = + new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); /** Creates a new RomiDrivetrain. */ public RomiDrivetrain() { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romitimed/RomiDrivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romitimed/RomiDrivetrain.java index e4f85599cea..a0f4f47b38b 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romitimed/RomiDrivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romitimed/RomiDrivetrain.java @@ -23,7 +23,8 @@ public class RomiDrivetrain { private final Encoder m_rightEncoder = new Encoder(6, 7); // Set up the differential drive controller - private final DifferentialDrive m_diffDrive = new DifferentialDrive(m_leftMotor, m_rightMotor); + private final DifferentialDrive m_diffDrive = + new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); /** Creates a new RomiDrivetrain. */ public RomiDrivetrain() { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/subsystems/XRPDrivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/subsystems/XRPDrivetrain.java index a3a1063a211..85febbed592 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/subsystems/XRPDrivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpcommandbased/subsystems/XRPDrivetrain.java @@ -27,7 +27,8 @@ public class XRPDrivetrain extends SubsystemBase { private final Encoder m_rightEncoder = new Encoder(6, 7); // Set up the differential drive controller - private final DifferentialDrive m_diffDrive = new DifferentialDrive(m_leftMotor, m_rightMotor); + private final DifferentialDrive m_diffDrive = + new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); /** Creates a new XRPDrivetrain. */ public XRPDrivetrain() { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/XRPDrivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/XRPDrivetrain.java index 8f73baec50d..a2459558056 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/XRPDrivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrpeducational/XRPDrivetrain.java @@ -26,7 +26,8 @@ public class XRPDrivetrain { private final Encoder m_rightEncoder = new Encoder(6, 7); // Set up the differential drive controller - private final DifferentialDrive m_diffDrive = new DifferentialDrive(m_leftMotor, m_rightMotor); + private final DifferentialDrive m_diffDrive = + new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); /** Creates a new XRPDrivetrain. */ public XRPDrivetrain() { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrptimed/XRPDrivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrptimed/XRPDrivetrain.java index e66d750d092..790acc7801b 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrptimed/XRPDrivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/xrptimed/XRPDrivetrain.java @@ -26,7 +26,8 @@ public class XRPDrivetrain { private final Encoder m_rightEncoder = new Encoder(6, 7); // Set up the differential drive controller - private final DifferentialDrive m_diffDrive = new DifferentialDrive(m_leftMotor, m_rightMotor); + private final DifferentialDrive m_diffDrive = + new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); /** Creates a new XRPDrivetrain. */ public XRPDrivetrain() { diff --git a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/MotorEncoderFixture.java b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/MotorEncoderFixture.java index ca93c4a2ac1..d6322913dc9 100644 --- a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/MotorEncoderFixture.java +++ b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/MotorEncoderFixture.java @@ -22,6 +22,7 @@ * fixture. This allows tests to be mailable so that you can easily reconfigure the physical testbed * without breaking the tests. */ +@SuppressWarnings("removal") public abstract class MotorEncoderFixture implements ITestFixture { private static final Logger logger = Logger.getLogger(MotorEncoderFixture.class.getName()); private boolean m_initialized = false; diff --git a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/TestBench.java b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/TestBench.java index 508c432cbfc..cba5a357132 100644 --- a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/TestBench.java +++ b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/TestBench.java @@ -52,6 +52,7 @@ public final class TestBench { public static final int DIOCrossConnectA1 = 6; /** The Singleton instance of the Test Bench. */ + @SuppressWarnings("unused") private static TestBench instance = null; /** diff --git a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/TestSuite.java b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/TestSuite.java index befc0668bbd..232781013e8 100644 --- a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/TestSuite.java +++ b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/TestSuite.java @@ -50,7 +50,10 @@ public class TestSuite extends AbstractTestSuite { TestBench.out().println("Starting Tests"); } + @SuppressWarnings("unused") private static final Logger WPILIBJ_ROOT_LOGGER = Logger.getLogger("edu.wpi.first.wpilibj"); + + @SuppressWarnings("unused") private static final Logger WPILIBJ_COMMAND_ROOT_LOGGER = Logger.getLogger("edu.wpi.first.wpilibj.command"); diff --git a/wpimath/src/generate/main/java/Nat.java.jinja b/wpimath/src/generate/main/java/Nat.java.jinja index 7a3fc5ad64b..66f06390c22 100644 --- a/wpimath/src/generate/main/java/Nat.java.jinja +++ b/wpimath/src/generate/main/java/Nat.java.jinja @@ -26,6 +26,11 @@ public interface Nat { */ int getNum(); {% for num in nums %} + /** + * Returns the Nat instance for {{ num }}. + * + * @return The Nat instance for {{ num }}. + */ static Nat N{{ num }}() { return N{{ num }}.instance; } diff --git a/wpimath/src/generated/main/java/edu/wpi/first/math/Nat.java b/wpimath/src/generated/main/java/edu/wpi/first/math/Nat.java index dc8d0add6fe..f6b0b81250b 100644 --- a/wpimath/src/generated/main/java/edu/wpi/first/math/Nat.java +++ b/wpimath/src/generated/main/java/edu/wpi/first/math/Nat.java @@ -45,86 +45,191 @@ public interface Nat { */ int getNum(); + /** + * Returns the Nat instance for 0. + * + * @return The Nat instance for 0. + */ static Nat N0() { return N0.instance; } + /** + * Returns the Nat instance for 1. + * + * @return The Nat instance for 1. + */ static Nat N1() { return N1.instance; } + /** + * Returns the Nat instance for 2. + * + * @return The Nat instance for 2. + */ static Nat N2() { return N2.instance; } + /** + * Returns the Nat instance for 3. + * + * @return The Nat instance for 3. + */ static Nat N3() { return N3.instance; } + /** + * Returns the Nat instance for 4. + * + * @return The Nat instance for 4. + */ static Nat N4() { return N4.instance; } + /** + * Returns the Nat instance for 5. + * + * @return The Nat instance for 5. + */ static Nat N5() { return N5.instance; } + /** + * Returns the Nat instance for 6. + * + * @return The Nat instance for 6. + */ static Nat N6() { return N6.instance; } + /** + * Returns the Nat instance for 7. + * + * @return The Nat instance for 7. + */ static Nat N7() { return N7.instance; } + /** + * Returns the Nat instance for 8. + * + * @return The Nat instance for 8. + */ static Nat N8() { return N8.instance; } + /** + * Returns the Nat instance for 9. + * + * @return The Nat instance for 9. + */ static Nat N9() { return N9.instance; } + /** + * Returns the Nat instance for 10. + * + * @return The Nat instance for 10. + */ static Nat N10() { return N10.instance; } + /** + * Returns the Nat instance for 11. + * + * @return The Nat instance for 11. + */ static Nat N11() { return N11.instance; } + /** + * Returns the Nat instance for 12. + * + * @return The Nat instance for 12. + */ static Nat N12() { return N12.instance; } + /** + * Returns the Nat instance for 13. + * + * @return The Nat instance for 13. + */ static Nat N13() { return N13.instance; } + /** + * Returns the Nat instance for 14. + * + * @return The Nat instance for 14. + */ static Nat N14() { return N14.instance; } + /** + * Returns the Nat instance for 15. + * + * @return The Nat instance for 15. + */ static Nat N15() { return N15.instance; } + /** + * Returns the Nat instance for 16. + * + * @return The Nat instance for 16. + */ static Nat N16() { return N16.instance; } + /** + * Returns the Nat instance for 17. + * + * @return The Nat instance for 17. + */ static Nat N17() { return N17.instance; } + /** + * Returns the Nat instance for 18. + * + * @return The Nat instance for 18. + */ static Nat N18() { return N18.instance; } + /** + * Returns the Nat instance for 19. + * + * @return The Nat instance for 19. + */ static Nat N19() { return N19.instance; } + /** + * Returns the Nat instance for 20. + * + * @return The Nat instance for 20. + */ static Nat N20() { return N20.instance; } diff --git a/wpimath/src/main/java/edu/wpi/first/math/ComputerVisionUtil.java b/wpimath/src/main/java/edu/wpi/first/math/ComputerVisionUtil.java index 7b1c3776808..609cdf5e5ae 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/ComputerVisionUtil.java +++ b/wpimath/src/main/java/edu/wpi/first/math/ComputerVisionUtil.java @@ -7,6 +7,7 @@ import edu.wpi.first.math.geometry.Pose3d; import edu.wpi.first.math.geometry.Transform3d; +/** Computer vision utility functions. */ public final class ComputerVisionUtil { private ComputerVisionUtil() { throw new AssertionError("utility class"); diff --git a/wpimath/src/main/java/edu/wpi/first/math/DARE.java b/wpimath/src/main/java/edu/wpi/first/math/DARE.java index ad07d056e7d..44c488dc526 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/DARE.java +++ b/wpimath/src/main/java/edu/wpi/first/math/DARE.java @@ -6,6 +6,7 @@ import org.ejml.simple.SimpleMatrix; +/** DARE solver utility functions. */ public final class DARE { private DARE() { throw new UnsupportedOperationException("This is a utility class!"); diff --git a/wpimath/src/main/java/edu/wpi/first/math/InterpolatingMatrixTreeMap.java b/wpimath/src/main/java/edu/wpi/first/math/InterpolatingMatrixTreeMap.java index a7b922c9696..a0aedef2dcd 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/InterpolatingMatrixTreeMap.java +++ b/wpimath/src/main/java/edu/wpi/first/math/InterpolatingMatrixTreeMap.java @@ -9,10 +9,17 @@ /** * Interpolating Tree Maps are used to get values at points that are not defined by making a guess * from points that are defined. This uses linear interpolation. + * + * @param Key type. + * @param Number of matrix rows. + * @param Number of matrix columns. */ public class InterpolatingMatrixTreeMap { private final TreeMap> m_map = new TreeMap<>(); + /** Default constructor. */ + public InterpolatingMatrixTreeMap() {} + /** * Inserts a key-value pair. * diff --git a/wpimath/src/main/java/edu/wpi/first/math/MatBuilder.java b/wpimath/src/main/java/edu/wpi/first/math/MatBuilder.java index 80c9d0dc9d6..46e42f47f4e 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/MatBuilder.java +++ b/wpimath/src/main/java/edu/wpi/first/math/MatBuilder.java @@ -40,7 +40,10 @@ public static final Matrix fill( return new Matrix<>(new SimpleMatrix(rows.getNum(), cols.getNum(), true, data)); } + /** Number of rows. */ protected final Nat m_rows; + + /** Number of columns. */ protected final Nat m_cols; /** diff --git a/wpimath/src/main/java/edu/wpi/first/math/MathShared.java b/wpimath/src/main/java/edu/wpi/first/math/MathShared.java index 3c2b8433886..ad4e2e81e6c 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/MathShared.java +++ b/wpimath/src/main/java/edu/wpi/first/math/MathShared.java @@ -4,6 +4,7 @@ package edu.wpi.first.math; +/** WPIMath utility functions. */ public interface MathShared { /** * Report an error. diff --git a/wpimath/src/main/java/edu/wpi/first/math/MathSharedStore.java b/wpimath/src/main/java/edu/wpi/first/math/MathSharedStore.java index d6e3e9655fb..d490a6e44ef 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/MathSharedStore.java +++ b/wpimath/src/main/java/edu/wpi/first/math/MathSharedStore.java @@ -6,6 +6,7 @@ import edu.wpi.first.util.WPIUtilJNI; +/** Storage for MathShared object. */ public final class MathSharedStore { private static MathShared mathShared; diff --git a/wpimath/src/main/java/edu/wpi/first/math/MathUsageId.java b/wpimath/src/main/java/edu/wpi/first/math/MathUsageId.java index a3cc2998754..c3449cfc61c 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/MathUsageId.java +++ b/wpimath/src/main/java/edu/wpi/first/math/MathUsageId.java @@ -4,15 +4,35 @@ package edu.wpi.first.math; +/** WPIMath usage reporting IDs. */ public enum MathUsageId { + /** DifferentialDriveKinematics. */ kKinematics_DifferentialDrive, + + /** MecanumDriveKinematics. */ kKinematics_MecanumDrive, + + /** SwerveDriveKinematics. */ kKinematics_SwerveDrive, + + /** TrapezoidProfile. */ kTrajectory_TrapezoidProfile, + + /** LinearFilter. */ kFilter_Linear, + + /** DifferentialDriveOdometry. */ kOdometry_DifferentialDrive, + + /** SwerveDriveOdometry. */ kOdometry_SwerveDrive, + + /** MecanumDriveOdometry. */ kOdometry_MecanumDrive, + + /** PIDController. */ kController_PIDController2, + + /** ProfiledPIDController. */ kController_ProfiledPIDController, } diff --git a/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java b/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java index 3785780a8d0..044cddfab68 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java +++ b/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java @@ -4,6 +4,7 @@ package edu.wpi.first.math; +/** Math utility functions. */ public final class MathUtil { private MathUtil() { throw new AssertionError("utility class"); diff --git a/wpimath/src/main/java/edu/wpi/first/math/Matrix.java b/wpimath/src/main/java/edu/wpi/first/math/Matrix.java index 80700d40276..c3f796ed0ff 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/Matrix.java +++ b/wpimath/src/main/java/edu/wpi/first/math/Matrix.java @@ -24,6 +24,7 @@ * @param The number of columns in this matrix. */ public class Matrix { + /** Storage for underlying EJML matrix. */ protected final SimpleMatrix m_storage; /** diff --git a/wpimath/src/main/java/edu/wpi/first/math/Num.java b/wpimath/src/main/java/edu/wpi/first/math/Num.java index ef0fd2d815f..aa31dd79915 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/Num.java +++ b/wpimath/src/main/java/edu/wpi/first/math/Num.java @@ -6,6 +6,9 @@ /** A number expressed as a java class. */ public abstract class Num { + /** Default constructor. */ + public Num() {} + /** * The number this is backing. * diff --git a/wpimath/src/main/java/edu/wpi/first/math/Pair.java b/wpimath/src/main/java/edu/wpi/first/math/Pair.java index 8ddf1ae37be..f854894ff61 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/Pair.java +++ b/wpimath/src/main/java/edu/wpi/first/math/Pair.java @@ -4,23 +4,54 @@ package edu.wpi.first.math; +/** + * Represents a pair of two objects. + * + * @param The first object's type. + * @param The second object's type. + */ public class Pair { private final A m_first; private final B m_second; + /** + * Constructs a pair. + * + * @param first The first object. + * @param second The second object. + */ public Pair(A first, B second) { m_first = first; m_second = second; } + /** + * Returns the first object. + * + * @return The first object. + */ public A getFirst() { return m_first; } + /** + * Returns the second object. + * + * @return The second object. + */ public B getSecond() { return m_second; } + /** + * Returns a pair comprised of the two given objects. + * + * @param The first object's type. + * @param The second object's type. + * @param a The first object. + * @param b The second object. + * @return A pair comprised of the two given objects. + */ public static Pair of(A a, B b) { return new Pair<>(a, b); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/StateSpaceUtil.java b/wpimath/src/main/java/edu/wpi/first/math/StateSpaceUtil.java index 27ed9e0bb09..4e003747693 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/StateSpaceUtil.java +++ b/wpimath/src/main/java/edu/wpi/first/math/StateSpaceUtil.java @@ -11,6 +11,7 @@ import java.util.Random; import org.ejml.simple.SimpleMatrix; +/** State-space utilities. */ public final class StateSpaceUtil { private static Random rand = new Random(); @@ -79,7 +80,7 @@ public static Matrix makeCostMatrix( if (tolerances.get(i, 0) == Double.POSITIVE_INFINITY) { result.set(i, i, 0.0); } else { - result.set(i, i, 1.0 / (Math.pow(tolerances.get(i, 0), 2))); + result.set(i, i, 1.0 / Math.pow(tolerances.get(i, 0), 2)); } } @@ -139,7 +140,7 @@ public static Matrix poseToVector(Pose2d pose) { * @param u The input to clamp. * @param umin The minimum input magnitude. * @param umax The maximum input magnitude. - * @param The number of inputs. + * @param Number of inputs. * @return The clamped input. */ public static Matrix clampInputMaxMagnitude( @@ -157,7 +158,7 @@ public static Matrix clampInputMaxMagnitude( * * @param u The input vector. * @param maxMagnitude The maximum magnitude any input can have. - * @param The number of inputs. + * @param Number of inputs. * @return The normalizedInput */ public static Matrix desaturateInputVector( diff --git a/wpimath/src/main/java/edu/wpi/first/math/WPIMathJNI.java b/wpimath/src/main/java/edu/wpi/first/math/WPIMathJNI.java index ef47a3b3c24..a60bcbae5fc 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/WPIMathJNI.java +++ b/wpimath/src/main/java/edu/wpi/first/math/WPIMathJNI.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; +/** WPIMath JNI. */ public final class WPIMathJNI { static boolean libraryLoaded = false; static RuntimeLoader loader = null; @@ -385,15 +386,32 @@ public static native double[] logPose3d( */ public static native String serializeTrajectory(double[] elements); + /** 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() {} } + + /** Utility class. */ + private WPIMathJNI() {} } diff --git a/wpimath/src/main/java/edu/wpi/first/math/controller/ArmFeedforward.java b/wpimath/src/main/java/edu/wpi/first/math/controller/ArmFeedforward.java index 5e2be2ad6ac..d7a695fced4 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/controller/ArmFeedforward.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/ArmFeedforward.java @@ -13,14 +13,23 @@ * A helper class that computes feedforward outputs for a simple arm (modeled as a motor acting * against the force of gravity on a beam suspended at an angle). */ -public class ArmFeedforward - implements ProtobufSerializable, StructSerializable { +public class ArmFeedforward implements ProtobufSerializable, StructSerializable { + /** The static gain, in volts. */ public final double ks; + + /** The gravity gain, in volts. */ public final double kg; + + /** The velocity gain, in volt seconds per radian. */ public final double kv; + + /** The acceleration gain, in volt seconds² per radian. */ public final double ka; + /** Arm feedforward protobuf for serialization. */ public static final ArmFeedforwardProto proto = new ArmFeedforwardProto(); + + /** Arm feedforward struct for serialization. */ public static final ArmFeedforwardStruct struct = new ArmFeedforwardStruct(); /** diff --git a/wpimath/src/main/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforward.java b/wpimath/src/main/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforward.java index 50ff576f952..9b14542207f 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforward.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforward.java @@ -27,6 +27,9 @@ * *

For more on the underlying math, read https://file.tavsys.net/control/controls-engineering-in-frc.pdf. + * + * @param Number of states. + * @param Number of inputs. */ public class ControlAffinePlantInversionFeedforward { /** The current reference state. */ @@ -178,7 +181,7 @@ public Matrix calculate(Matrix nextR) { * @return The calculated feedforward. */ public Matrix calculate(Matrix r, Matrix nextR) { - var rDot = (nextR.minus(r)).div(m_dt); + var rDot = nextR.minus(r).div(m_dt); // ṙ = f(r) + Bu // Bu = ṙ − f(r) diff --git a/wpimath/src/main/java/edu/wpi/first/math/controller/DifferentialDriveWheelVoltages.java b/wpimath/src/main/java/edu/wpi/first/math/controller/DifferentialDriveWheelVoltages.java index 526d68ac084..1569be06856 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/controller/DifferentialDriveWheelVoltages.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/DifferentialDriveWheelVoltages.java @@ -10,19 +10,30 @@ import edu.wpi.first.util.struct.StructSerializable; /** Motor voltages for a differential drive. */ -public class DifferentialDriveWheelVoltages - implements ProtobufSerializable, - StructSerializable { +public class DifferentialDriveWheelVoltages implements ProtobufSerializable, StructSerializable { + /** Left wheel voltage. */ public double left; + + /** Right wheel voltage. */ public double right; + /** DifferentialDriveWheelVoltages protobuf for serialization. */ public static final DifferentialDriveWheelVoltagesProto proto = new DifferentialDriveWheelVoltagesProto(); + + /** DifferentialDriveWheelVoltages struct for serialization. */ public static final DifferentialDriveWheelVoltagesStruct struct = new DifferentialDriveWheelVoltagesStruct(); + /** Default constructor. */ public DifferentialDriveWheelVoltages() {} + /** + * Constructs a DifferentialDriveWheelVoltages. + * + * @param left Left wheel voltage. + * @param right Right wheel voltage. + */ public DifferentialDriveWheelVoltages(double left, double right) { this.left = left; this.right = right; diff --git a/wpimath/src/main/java/edu/wpi/first/math/controller/ElevatorFeedforward.java b/wpimath/src/main/java/edu/wpi/first/math/controller/ElevatorFeedforward.java index 9a6b1ef8e97..854462a652c 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/controller/ElevatorFeedforward.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/ElevatorFeedforward.java @@ -16,14 +16,23 @@ * A helper class that computes feedforward outputs for a simple elevator (modeled as a motor acting * against the force of gravity). */ -public class ElevatorFeedforward - implements ProtobufSerializable, StructSerializable { +public class ElevatorFeedforward implements ProtobufSerializable, StructSerializable { + /** The static gain. */ public final double ks; + + /** The gravity gain. */ public final double kg; + + /** The velocity gain. */ public final double kv; + + /** The acceleration gain. */ public final double ka; + /** ElevatorFeedforward protobuf for serialization. */ public static final ElevatorFeedforwardProto proto = new ElevatorFeedforwardProto(); + + /** ElevatorFeedforward struct for serialization. */ public static final ElevatorFeedforwardStruct struct = new ElevatorFeedforwardStruct(); /** diff --git a/wpimath/src/main/java/edu/wpi/first/math/controller/ImplicitModelFollower.java b/wpimath/src/main/java/edu/wpi/first/math/controller/ImplicitModelFollower.java index de370e9af1a..29bf0a02be7 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/controller/ImplicitModelFollower.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/ImplicitModelFollower.java @@ -19,6 +19,10 @@ * *

For more on the underlying math, read appendix B.3 in https://file.tavsys.net/control/controls-engineering-in-frc.pdf. + * + * @param Number of states. + * @param Number of inputs. + * @param Number of outputs. */ public class ImplicitModelFollower { // Computed controller output diff --git a/wpimath/src/main/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforward.java b/wpimath/src/main/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforward.java index ba395aabbcc..f71465a82a2 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforward.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforward.java @@ -19,6 +19,10 @@ * *

For more on the underlying math, read https://file.tavsys.net/control/controls-engineering-in-frc.pdf. + * + * @param Number of states. + * @param Number of inputs. + * @param Number of outputs. */ public class LinearPlantInversionFeedforward< States extends Num, Inputs extends Num, Outputs extends Num> { diff --git a/wpimath/src/main/java/edu/wpi/first/math/controller/LinearQuadraticRegulator.java b/wpimath/src/main/java/edu/wpi/first/math/controller/LinearQuadraticRegulator.java index 800ea4007a6..660fa8fefac 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/controller/LinearQuadraticRegulator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/LinearQuadraticRegulator.java @@ -21,6 +21,10 @@ * *

For more on the underlying math, read https://file.tavsys.net/control/controls-engineering-in-frc.pdf. + * + * @param Number of states. + * @param Number of inputs. + * @param Number of outputs. */ public class LinearQuadraticRegulator { /** The current reference state. */ diff --git a/wpimath/src/main/java/edu/wpi/first/math/controller/SimpleMotorFeedforward.java b/wpimath/src/main/java/edu/wpi/first/math/controller/SimpleMotorFeedforward.java index f19e8366172..2b25bccbf8f 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/controller/SimpleMotorFeedforward.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/SimpleMotorFeedforward.java @@ -10,8 +10,13 @@ /** A helper class that computes feedforward outputs for a simple permanent-magnet DC motor. */ public class SimpleMotorFeedforward { + /** The static gain. */ public final double ks; + + /** The velocity gain. */ public final double kv; + + /** The acceleration gain. */ public final double ka; /** diff --git a/wpimath/src/main/java/edu/wpi/first/math/estimator/AngleStatistics.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/AngleStatistics.java index 0f91a1db83a..8a61526e8a1 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/estimator/AngleStatistics.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/AngleStatistics.java @@ -11,10 +11,10 @@ import java.util.function.BiFunction; import org.ejml.simple.SimpleMatrix; +/** Angle statistics functions. */ public final class AngleStatistics { - private AngleStatistics() { - // Utility class - } + /** Utility class. */ + private AngleStatistics() {} /** * Subtracts a and b while normalizing the resulting value in the selected row as if it were an diff --git a/wpimath/src/main/java/edu/wpi/first/math/estimator/ExtendedKalmanFilter.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/ExtendedKalmanFilter.java index db246dcab3c..055bf9939af 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/estimator/ExtendedKalmanFilter.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/ExtendedKalmanFilter.java @@ -33,6 +33,10 @@ *

For more on the underlying math, read https://file.tavsys.net/control/controls-engineering-in-frc.pdf * chapter 9 "Stochastic control theory". + * + * @param Number of states. + * @param Number of inputs. + * @param Number of outputs. */ public class ExtendedKalmanFilter implements KalmanTypeFilter { diff --git a/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilter.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilter.java index 8ca1db0a86f..9e286255df7 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilter.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilter.java @@ -28,6 +28,10 @@ *

For more on the underlying math, read https://file.tavsys.net/control/controls-engineering-in-frc.pdf * chapter 9 "Stochastic control theory". + * + * @param Number of states. + * @param Number of inputs. + * @param Number of outputs. */ public class KalmanFilter implements KalmanTypeFilter { diff --git a/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilterLatencyCompensator.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilterLatencyCompensator.java index 15e7bd4b6d3..ad4db523377 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilterLatencyCompensator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilterLatencyCompensator.java @@ -13,11 +13,19 @@ import java.util.Map; import java.util.function.BiConsumer; +/** + * This class incorporates time-delayed measurements into a Kalman filter's state estimate. + * + * @param The number of states. + * @param The number of inputs. + * @param The number of outputs. + */ public class KalmanFilterLatencyCompensator { private static final int kMaxPastObserverStates = 300; private final List> m_pastObserverSnapshots; + /** Default constructor. */ KalmanFilterLatencyCompensator() { m_pastObserverSnapshots = new ArrayList<>(); } @@ -164,9 +172,16 @@ public void applyPastGlobalMeasurement( /** This class contains all the information about our observer at a given time. */ public class ObserverSnapshot { + /** The state estimate. */ public final Matrix xHat; + + /** The error covariance. */ public final Matrix errorCovariances; + + /** The inputs. */ public final Matrix inputs; + + /** The local measurements. */ public final Matrix localMeasurements; private ObserverSnapshot( diff --git a/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanTypeFilter.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanTypeFilter.java index 7b14839f2a2..ff9856c32df 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanTypeFilter.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanTypeFilter.java @@ -8,24 +8,83 @@ import edu.wpi.first.math.Num; import edu.wpi.first.math.numbers.N1; +/** + * Interface for Kalman filters for use with KalmanFilterLatencyCompensator. + * + * @param The number of states. + * @param The number of inputs. + * @param The number of outputs. + */ public interface KalmanTypeFilter { + /** + * Returns the error covariance matrix. + * + * @return The error covariance matrix. + */ Matrix getP(); + /** + * Returns an element of the error covariance matrix. + * + * @param i The row. + * @param j The column. + * @return An element of the error covariance matrix. + */ double getP(int i, int j); + /** + * Sets the error covariance matrix. + * + * @param newP The error covariance matrix. + */ void setP(Matrix newP); + /** + * Returns the state estimate. + * + * @return The state estimate. + */ Matrix getXhat(); + /** + * Returns an element of the state estimate. + * + * @param i The row. + * @return An element of the state estimate. + */ double getXhat(int i); + /** + * Sets the state estimate. + * + * @param xHat The state estimate. + */ void setXhat(Matrix xHat); + /** + * Sets an element of the state estimate. + * + * @param i The row. + * @param value The value. + */ void setXhat(int i, double value); + /** Resets the observer. */ void reset(); + /** + * Project the model into the future with a new control input u. + * + * @param u New control input from controller. + * @param dtSeconds Timestep for prediction. + */ void predict(Matrix u, double dtSeconds); + /** + * Correct the state estimate x-hat using the measurements in y. + * + * @param u Same control input used in the predict step. + * @param y Measurement vector. + */ void correct(Matrix u, Matrix y); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/estimator/MerweScaledSigmaPoints.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/MerweScaledSigmaPoints.java index 24835f6c341..24fa771f339 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/estimator/MerweScaledSigmaPoints.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/MerweScaledSigmaPoints.java @@ -21,6 +21,8 @@ * *

[1] R. Van der Merwe "Sigma-Point Kalman Filters for Probabilitic Inference in Dynamic * State-Space Models" (Doctoral dissertation) + * + * @param The dimensionality of the state. 2 * States + 1 weights will be generated. */ public class MerweScaledSigmaPoints { private final double m_alpha; diff --git a/wpimath/src/main/java/edu/wpi/first/math/estimator/PoseEstimator.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/PoseEstimator.java index 1d9a54b931a..14eee8f190a 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/estimator/PoseEstimator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/PoseEstimator.java @@ -34,6 +34,8 @@ * *

{@link PoseEstimator#addVisionMeasurement} can be called as infrequently as you want; if you * never call it then this class will behave exactly like regular encoder odometry. + * + * @param Wheel positions type. */ public class PoseEstimator> { private final Kinematics m_kinematics; diff --git a/wpimath/src/main/java/edu/wpi/first/math/estimator/SteadyStateKalmanFilter.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/SteadyStateKalmanFilter.java index 801c601143d..e29032d7cba 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/estimator/SteadyStateKalmanFilter.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/SteadyStateKalmanFilter.java @@ -31,6 +31,10 @@ *

For more on the underlying math, read https://file.tavsys.net/control/controls-engineering-in-frc.pdf * chapter 9 "Stochastic control theory". + * + * @param Number of states. + * @param Number of inputs. + * @param Number of outputs. */ public class SteadyStateKalmanFilter { private final Nat m_states; @@ -110,11 +114,12 @@ public SteadyStateKalmanFilter( // K = (Sáµ€.solve(CPáµ€))áµ€ m_K = new Matrix<>( - S.transpose().getStorage().solve((C.times(P.transpose())).getStorage()).transpose()); + S.transpose().getStorage().solve(C.times(P.transpose()).getStorage()).transpose()); reset(); } + /** Resets the observer. */ public final void reset() { m_xHat = new Matrix<>(m_states, Nat.N1()); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/estimator/UnscentedKalmanFilter.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/UnscentedKalmanFilter.java index c982d08450f..c64ec85cf9b 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/estimator/UnscentedKalmanFilter.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/UnscentedKalmanFilter.java @@ -38,6 +38,10 @@ *

This class implements a square-root-form unscented Kalman filter (SR-UKF). For more * information about the SR-UKF, see https://www.researchgate.net/publication/3908304. + * + * @param Number of states. + * @param Number of inputs. + * @param Number of outputs. */ public class UnscentedKalmanFilter implements KalmanTypeFilter { diff --git a/wpimath/src/main/java/edu/wpi/first/math/filter/Debouncer.java b/wpimath/src/main/java/edu/wpi/first/math/filter/Debouncer.java index 3472198d4b4..c113d9655ae 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/filter/Debouncer.java +++ b/wpimath/src/main/java/edu/wpi/first/math/filter/Debouncer.java @@ -11,9 +11,13 @@ * baseline for a specified period of time before the filtered value changes. */ public class Debouncer { + /** Type of debouncing to perform. */ public enum DebounceType { + /** Rising edge. */ kRising, + /** Falling edge. */ kFalling, + /** Both rising and falling edges. */ kBoth } diff --git a/wpimath/src/main/java/edu/wpi/first/math/filter/LinearFilter.java b/wpimath/src/main/java/edu/wpi/first/math/filter/LinearFilter.java index 7a568c86134..c26270fa494 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/filter/LinearFilter.java +++ b/wpimath/src/main/java/edu/wpi/first/math/filter/LinearFilter.java @@ -246,6 +246,43 @@ public void reset() { m_outputs.clear(); } + /** + * Resets the filter state, initializing internal buffers to the provided values. + * + *

These are the expected lengths of the buffers, depending on what type of linear filter used: + * + * + * + * + * + * + * + * + * + * + *
TypeInput Buffer LengthOutput Buffer Length
Unspecifiedlength of {@code ffGains}length of {@code fbGains}
Single Pole IIR11
High-Pass21
Moving Average{@code taps}0
Finite Differencelength of {@code stencil}0
Backward Finite Difference{@code samples}0
+ * + * @param inputBuffer Values to initialize input buffer. + * @param outputBuffer Values to initialize output buffer. + * @throws IllegalArgumentException if length of inputBuffer or outputBuffer does not match the + * length of ffGains and fbGains provided in the constructor. + */ + public void reset(double[] inputBuffer, double[] outputBuffer) { + // Clear buffers + reset(); + + if (inputBuffer.length != m_inputGains.length || outputBuffer.length != m_outputGains.length) { + throw new IllegalArgumentException("Incorrect length of inputBuffer or outputBuffer"); + } + + for (double input : inputBuffer) { + m_inputs.addFirst(input); + } + for (double output : outputBuffer) { + m_outputs.addFirst(output); + } + } + /** * Calculates the next value of the filter. * diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose2d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose2d.java index e824cc38c96..97e83079993 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose2d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose2d.java @@ -25,8 +25,7 @@ /** Represents a 2D pose containing translational and rotational elements. */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE) -public class Pose2d - implements Interpolatable, ProtobufSerializable, StructSerializable { +public class Pose2d implements Interpolatable, ProtobufSerializable, StructSerializable { private final Translation2d m_translation; private final Rotation2d m_rotation; @@ -327,6 +326,9 @@ public Pose2d interpolate(Pose2d endValue, double t) { } } - public static final Pose2dStruct struct = new Pose2dStruct(); + /** Pose2d protobuf for serialization. */ public static final Pose2dProto proto = new Pose2dProto(); + + /** Pose2d struct for serialization. */ + public static final Pose2dStruct struct = new Pose2dStruct(); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose3d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose3d.java index ef3597d2f02..282420fefbf 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose3d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose3d.java @@ -19,8 +19,7 @@ /** Represents a 3D pose containing translational and rotational elements. */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE) -public class Pose3d - implements Interpolatable, ProtobufSerializable, StructSerializable { +public class Pose3d implements Interpolatable, ProtobufSerializable, StructSerializable { private final Translation3d m_translation; private final Rotation3d m_rotation; @@ -324,6 +323,9 @@ public Pose3d interpolate(Pose3d endValue, double t) { } } - public static final Pose3dStruct struct = new Pose3dStruct(); + /** Pose3d protobuf for serialization. */ public static final Pose3dProto proto = new Pose3dProto(); + + /** Pose3d struct for serialization. */ + public static final Pose3dStruct struct = new Pose3dStruct(); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Quaternion.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Quaternion.java index 41ac6b1f756..ea0be3cfdd6 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Quaternion.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Quaternion.java @@ -17,10 +17,10 @@ import edu.wpi.first.util.struct.StructSerializable; import java.util.Objects; +/** Represents a quaternion. */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE) -public class Quaternion - implements ProtobufSerializable, StructSerializable { +public class Quaternion implements ProtobufSerializable, StructSerializable { // Scalar r in versor form private final double m_w; @@ -406,6 +406,9 @@ public Vector toRotationVector() { return VecBuilder.fill(coeff * getX(), coeff * getY(), coeff * getZ()); } - public static final QuaternionStruct struct = new QuaternionStruct(); + /** Quaternion protobuf for serialization. */ public static final QuaternionProto proto = new QuaternionProto(); + + /** Quaternion struct for serialization. */ + public static final QuaternionStruct struct = new QuaternionStruct(); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java index 71c1e494ceb..1106a103a52 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java @@ -31,9 +31,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE) public class Rotation2d - implements Interpolatable, - ProtobufSerializable, - StructSerializable { + implements Interpolatable, ProtobufSerializable, StructSerializable { private final double m_value; private final double m_cos; private final double m_sin; @@ -277,6 +275,9 @@ public Rotation2d interpolate(Rotation2d endValue, double t) { return plus(endValue.minus(this).times(MathUtil.clamp(t, 0, 1))); } - public static final Rotation2dStruct struct = new Rotation2dStruct(); + /** Rotation2d protobuf for serialization. */ public static final Rotation2dProto proto = new Rotation2dProto(); + + /** Rotation2d struct for serialization. */ + public static final Rotation2dStruct struct = new Rotation2dStruct(); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation3d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation3d.java index 3cfcac6adf5..33fd34a3b27 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation3d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation3d.java @@ -27,9 +27,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE) public class Rotation3d - implements Interpolatable, - ProtobufSerializable, - StructSerializable { + implements Interpolatable, ProtobufSerializable, StructSerializable { private final Quaternion m_q; /** Constructs a Rotation3d with a default angle of 0 degrees. */ @@ -434,6 +432,9 @@ public Rotation3d interpolate(Rotation3d endValue, double t) { return plus(endValue.minus(this).times(MathUtil.clamp(t, 0, 1))); } - public static final Rotation3dStruct struct = new Rotation3dStruct(); + /** Rotation3d protobuf for serialization. */ public static final Rotation3dProto proto = new Rotation3dProto(); + + /** Rotation3d struct for serialization. */ + public static final Rotation3dStruct struct = new Rotation3dStruct(); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform2d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform2d.java index f67b06cbeff..68747af423a 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform2d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform2d.java @@ -15,8 +15,7 @@ import java.util.Objects; /** Represents a transformation for a Pose2d in the pose's frame. */ -public class Transform2d - implements ProtobufSerializable, StructSerializable { +public class Transform2d implements ProtobufSerializable, StructSerializable { private final Translation2d m_translation; private final Rotation2d m_rotation; @@ -185,6 +184,9 @@ public int hashCode() { return Objects.hash(m_translation, m_rotation); } - public static final Transform2dStruct struct = new Transform2dStruct(); + /** Transform2d protobuf for serialization. */ public static final Transform2dProto proto = new Transform2dProto(); + + /** Transform2d struct for serialization. */ + public static final Transform2dStruct struct = new Transform2dStruct(); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform3d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform3d.java index fcdeef25b8b..cdd021f8352 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform3d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform3d.java @@ -11,8 +11,7 @@ import java.util.Objects; /** Represents a transformation for a Pose3d in the pose's frame. */ -public class Transform3d - implements ProtobufSerializable, StructSerializable { +public class Transform3d implements ProtobufSerializable, StructSerializable { private final Translation3d m_translation; private final Rotation3d m_rotation; @@ -179,6 +178,9 @@ public int hashCode() { return Objects.hash(m_translation, m_rotation); } - public static final Transform3dStruct struct = new Transform3dStruct(); + /** Transform3d protobuf for serialization. */ public static final Transform3dProto proto = new Transform3dProto(); + + /** Transform3d struct for serialization. */ + public static final Transform3dStruct struct = new Transform3dStruct(); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java index 8af696124d9..f089f6690f3 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java @@ -32,9 +32,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE) public class Translation2d - implements Interpolatable, - ProtobufSerializable, - StructSerializable { + implements Interpolatable, ProtobufSerializable, StructSerializable { private final double m_x; private final double m_y; @@ -252,6 +250,9 @@ public Translation2d interpolate(Translation2d endValue, double t) { MathUtil.interpolate(this.getY(), endValue.getY(), t)); } - public static final Translation2dStruct struct = new Translation2dStruct(); + /** Translation2d protobuf for serialization. */ public static final Translation2dProto proto = new Translation2dProto(); + + /** Translation2d struct for serialization. */ + public static final Translation2dStruct struct = new Translation2dStruct(); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation3d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation3d.java index 1b88d18fe17..6d4fa9247e3 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation3d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation3d.java @@ -30,9 +30,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE) public class Translation3d - implements Interpolatable, - ProtobufSerializable, - StructSerializable { + implements Interpolatable, ProtobufSerializable, StructSerializable { private final double m_x; private final double m_y; private final double m_z; @@ -255,6 +253,9 @@ public Translation3d interpolate(Translation3d endValue, double t) { MathUtil.interpolate(this.getZ(), endValue.getZ(), t)); } - public static final Translation3dStruct struct = new Translation3dStruct(); + /** Translation3d protobuf for serialization. */ public static final Translation3dProto proto = new Translation3dProto(); + + /** Translation3d struct for serialization. */ + public static final Translation3dStruct struct = new Translation3dStruct(); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist2d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist2d.java index a656baa0169..53ab6ba9c2a 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist2d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist2d.java @@ -16,7 +16,7 @@ * *

A Twist can be used to represent a difference between two poses. */ -public class Twist2d implements ProtobufSerializable, StructSerializable { +public class Twist2d implements ProtobufSerializable, StructSerializable { /** Linear "dx" component. */ public double dx; @@ -26,6 +26,7 @@ public class Twist2d implements ProtobufSerializable, StructSerializabl /** Angular "dtheta" component (radians). */ public double dtheta; + /** Default constructor. */ public Twist2d() {} /** @@ -67,6 +68,9 @@ public int hashCode() { return Objects.hash(dx, dy, dtheta); } - public static final Twist2dStruct struct = new Twist2dStruct(); + /** Twist2d protobuf for serialization. */ public static final Twist2dProto proto = new Twist2dProto(); + + /** Twist2d struct for serialization. */ + public static final Twist2dStruct struct = new Twist2dStruct(); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist3d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist3d.java index e5f79e4bebd..80786029efd 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist3d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist3d.java @@ -16,7 +16,7 @@ * *

A Twist can be used to represent a difference between two poses. */ -public class Twist3d implements ProtobufSerializable, StructSerializable { +public class Twist3d implements ProtobufSerializable, StructSerializable { /** Linear "dx" component. */ public double dx; @@ -35,6 +35,7 @@ public class Twist3d implements ProtobufSerializable, StructSerializabl /** Rotation vector z component (radians). */ public double rz; + /** Default constructor. */ public Twist3d() {} /** @@ -87,6 +88,9 @@ public int hashCode() { return Objects.hash(dx, dy, dz, rx, ry, rz); } - public static final Twist3dStruct struct = new Twist3dStruct(); + /** Twist3d protobuf for serialization. */ public static final Twist3dProto proto = new Twist3dProto(); + + /** Twist3d struct for serialization. */ + public static final Twist3dStruct struct = new Twist3dStruct(); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/interpolation/InterpolatingDoubleTreeMap.java b/wpimath/src/main/java/edu/wpi/first/math/interpolation/InterpolatingDoubleTreeMap.java index bcd57a392e3..15c55d091c5 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/interpolation/InterpolatingDoubleTreeMap.java +++ b/wpimath/src/main/java/edu/wpi/first/math/interpolation/InterpolatingDoubleTreeMap.java @@ -9,6 +9,7 @@ * from points that are defined. This uses linear interpolation. */ public class InterpolatingDoubleTreeMap extends InterpolatingTreeMap { + /** Default constructor. */ public InterpolatingDoubleTreeMap() { super(InverseInterpolator.forDouble(), Interpolator.forDouble()); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/interpolation/Interpolator.java b/wpimath/src/main/java/edu/wpi/first/math/interpolation/Interpolator.java index be6d8a2d26b..5fe1a8f28a5 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/interpolation/Interpolator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/interpolation/Interpolator.java @@ -24,6 +24,11 @@ public interface Interpolator { */ T interpolate(T startValue, T endValue, double t); + /** + * Returns interpolator for Double. + * + * @return Interpolator for Double. + */ static Interpolator forDouble() { return MathUtil::interpolate; } diff --git a/wpimath/src/main/java/edu/wpi/first/math/interpolation/InverseInterpolator.java b/wpimath/src/main/java/edu/wpi/first/math/interpolation/InverseInterpolator.java index 8278af3f458..d6d0bc8c5be 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/interpolation/InverseInterpolator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/interpolation/InverseInterpolator.java @@ -24,6 +24,11 @@ public interface InverseInterpolator { */ double inverseInterpolate(T startValue, T endValue, T q); + /** + * Returns inverse interpolator for Double. + * + * @return Inverse interpolator for Double. + */ static InverseInterpolator forDouble() { return MathUtil::inverseInterpolate; } diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/ChassisSpeeds.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/ChassisSpeeds.java index 8304b137ede..33a5edca43e 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/ChassisSpeeds.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/ChassisSpeeds.java @@ -30,8 +30,7 @@ * component because it can never move sideways. Holonomic drivetrains such as swerve and mecanum * will often have all three components. */ -public class ChassisSpeeds - implements ProtobufSerializable, StructSerializable { +public class ChassisSpeeds implements ProtobufSerializable, StructSerializable { /** Velocity along the x-axis. (Fwd is +) */ public double vxMetersPerSecond; @@ -41,7 +40,10 @@ public class ChassisSpeeds /** Represents the angular velocity of the robot frame. (CCW is +) */ public double omegaRadiansPerSecond; + /** ChassisSpeeds protobuf for serialization. */ public static final ChassisSpeedsProto proto = new ChassisSpeedsProto(); + + /** ChassisSpeeds struct for serialization. */ public static final ChassisSpeedsStruct struct = new ChassisSpeedsStruct(); /** Constructs a ChassisSpeeds with zeros for dx, dy, and theta. */ diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematics.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematics.java index 7f39c8788f8..1d18b005daa 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematics.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematics.java @@ -26,12 +26,16 @@ */ public class DifferentialDriveKinematics implements Kinematics, - ProtobufSerializable, - StructSerializable { + ProtobufSerializable, + StructSerializable { + /** Differential drive trackwidth. */ public final double trackWidthMeters; + /** DifferentialDriveKinematics protobuf for serialization. */ public static final DifferentialDriveKinematicsProto proto = new DifferentialDriveKinematicsProto(); + + /** DifferentialDriveKinematics struct for serialization. */ public static final DifferentialDriveKinematicsStruct struct = new DifferentialDriveKinematicsStruct(); diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelPositions.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelPositions.java index c0a211e049b..fbdf54d8818 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelPositions.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelPositions.java @@ -11,6 +11,7 @@ import edu.wpi.first.units.Measure; import java.util.Objects; +/** Represents the wheel positions for a differential drive drivetrain. */ public class DifferentialDriveWheelPositions implements WheelPositions { /** Distance measured by the left side. */ diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeeds.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeeds.java index 5b4bb501e54..de188179534 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeeds.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeeds.java @@ -15,17 +15,18 @@ import edu.wpi.first.util.struct.StructSerializable; /** Represents the wheel speeds for a differential drive drivetrain. */ -public class DifferentialDriveWheelSpeeds - implements ProtobufSerializable, - StructSerializable { +public class DifferentialDriveWheelSpeeds implements ProtobufSerializable, StructSerializable { /** Speed of the left side of the robot. */ public double leftMetersPerSecond; /** Speed of the right side of the robot. */ public double rightMetersPerSecond; + /** DifferentialDriveWheelSpeeds protobuf for serialization. */ public static final DifferentialDriveWheelSpeedsProto proto = new DifferentialDriveWheelSpeedsProto(); + + /** DifferentialDriveWheelSpeeds struct for serialization. */ public static final DifferentialDriveWheelSpeedsStruct struct = new DifferentialDriveWheelSpeedsStruct(); diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveKinematics.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveKinematics.java index 14829afa365..44dbb6b25f0 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveKinematics.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveKinematics.java @@ -36,8 +36,8 @@ */ public class MecanumDriveKinematics implements Kinematics, - ProtobufSerializable, - StructSerializable { + ProtobufSerializable, + StructSerializable { private final SimpleMatrix m_inverseKinematics; private final SimpleMatrix m_forwardKinematics; @@ -48,7 +48,10 @@ public class MecanumDriveKinematics private Translation2d m_prevCoR = new Translation2d(); + /** MecanumDriveKinematics protobuf for serialization. */ public static final MecanumDriveKinematicsProto proto = new MecanumDriveKinematicsProto(); + + /** MecanumDriveKinematics struct for serialization. */ public static final MecanumDriveKinematicsStruct struct = new MecanumDriveKinematicsStruct(); /** @@ -217,18 +220,38 @@ private void setInverseKinematics( m_inverseKinematics.setRow(3, 0, 1, -1, -(rr.getX() + rr.getY())); } + /** + * Returns the front-left wheel translation. + * + * @return The front-left wheel translation. + */ public Translation2d getFrontLeft() { return m_frontLeftWheelMeters; } + /** + * Returns the front-right wheel translation. + * + * @return The front-right wheel translation. + */ public Translation2d getFrontRight() { return m_frontRightWheelMeters; } + /** + * Returns the rear-left wheel translation. + * + * @return The rear-left wheel translation. + */ public Translation2d getRearLeft() { return m_rearLeftWheelMeters; } + /** + * Returns the rear-right wheel translation. + * + * @return The rear-right wheel translation. + */ public Translation2d getRearRight() { return m_rearRightWheelMeters; } diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelPositions.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelPositions.java index c22de3a8c32..179e1bf14b5 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelPositions.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelPositions.java @@ -15,10 +15,11 @@ import edu.wpi.first.util.struct.StructSerializable; import java.util.Objects; +/** Represents the wheel positions for a mecanum drive drivetrain. */ public class MecanumDriveWheelPositions implements WheelPositions, - ProtobufSerializable, - StructSerializable { + ProtobufSerializable, + StructSerializable { /** Distance measured by the front left wheel. */ public double frontLeftMeters; @@ -31,9 +32,12 @@ public class MecanumDriveWheelPositions /** Distance measured by the rear right wheel. */ public double rearRightMeters; + /** MecanumDriveWheelPositions protobuf for serialization. */ + public static final MecanumDriveWheelPositionsProto proto = new MecanumDriveWheelPositionsProto(); + + /** MecanumDriveWheelPositions struct for serialization. */ public static final MecanumDriveWheelPositionsStruct struct = new MecanumDriveWheelPositionsStruct(); - public static final MecanumDriveWheelPositionsProto proto = new MecanumDriveWheelPositionsProto(); /** Constructs a MecanumDriveWheelPositions with zeros for all member fields. */ public MecanumDriveWheelPositions() {} diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeeds.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeeds.java index 73ccf295fb8..59a34558a1d 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeeds.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeeds.java @@ -14,9 +14,8 @@ import edu.wpi.first.util.protobuf.ProtobufSerializable; import edu.wpi.first.util.struct.StructSerializable; -public class MecanumDriveWheelSpeeds - implements ProtobufSerializable, - StructSerializable { +/** Represents the wheel speeds for a mecanum drive drivetrain. */ +public class MecanumDriveWheelSpeeds implements ProtobufSerializable, StructSerializable { /** Speed of the front left wheel. */ public double frontLeftMetersPerSecond; @@ -29,9 +28,12 @@ public class MecanumDriveWheelSpeeds /** Speed of the rear right wheel. */ public double rearRightMetersPerSecond; - public static final MecanumDriveWheelSpeedsStruct struct = new MecanumDriveWheelSpeedsStruct(); + /** MecanumDriveWheelSpeeds protobuf for serialization. */ public static final MecanumDriveWheelSpeedsProto proto = new MecanumDriveWheelSpeedsProto(); + /** MecanumDriveWheelSpeeds struct for serialization. */ + public static final MecanumDriveWheelSpeedsStruct struct = new MecanumDriveWheelSpeedsStruct(); + /** Constructs a MecanumDriveWheelSpeeds with zeros for all member fields. */ public MecanumDriveWheelSpeeds() {} diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/Odometry.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/Odometry.java index b2e5054eb83..427c92d8a18 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/Odometry.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/Odometry.java @@ -15,6 +15,8 @@ * *

Teams can use odometry during the autonomous period for complex tasks like path following. * Furthermore, odometry can be used for latency compensation when using computer-vision systems. + * + * @param Wheel positions type. */ public class Odometry> { private final Kinematics m_kinematics; diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java index 2498b337427..6648343f897 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java @@ -41,7 +41,9 @@ */ public class SwerveDriveKinematics implements Kinematics { + /** Wrapper class for swerve module states. */ public static class SwerveDriveWheelStates { + /** Swerve module states. */ public SwerveModuleState[] states; /** diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveWheelPositions.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveWheelPositions.java index e88f044803d..5d0f0af113a 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveWheelPositions.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveWheelPositions.java @@ -7,7 +7,9 @@ import java.util.Arrays; import java.util.Objects; +/** Represents the wheel positions for a swerve drive drivetrain. */ public class SwerveDriveWheelPositions implements WheelPositions { + /** The distances driven by the wheels. */ public SwerveModulePosition[] positions; /** diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModulePosition.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModulePosition.java index 02803900000..3041d42cd0f 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModulePosition.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModulePosition.java @@ -21,17 +21,20 @@ public class SwerveModulePosition implements Comparable, Interpolatable, - ProtobufSerializable, - StructSerializable { + ProtobufSerializable, + StructSerializable { /** Distance measured by the wheel of the module. */ public double distanceMeters; /** Angle of the module. */ public Rotation2d angle = Rotation2d.fromDegrees(0); - public static final SwerveModulePositionStruct struct = new SwerveModulePositionStruct(); + /** SwerveModulePosition protobuf for serialization. */ public static final SwerveModulePositionProto proto = new SwerveModulePositionProto(); + /** SwerveModulePosition struct for serialization. */ + public static final SwerveModulePositionStruct struct = new SwerveModulePositionStruct(); + /** Constructs a SwerveModulePosition with zeros for distance and angle. */ public SwerveModulePosition() {} diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModuleState.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModuleState.java index 329e6b1116b..9f1604a0d67 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModuleState.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModuleState.java @@ -18,18 +18,19 @@ /** Represents the state of one swerve module. */ public class SwerveModuleState - implements Comparable, - ProtobufSerializable, - StructSerializable { + implements Comparable, ProtobufSerializable, StructSerializable { /** Speed of the wheel of the module. */ public double speedMetersPerSecond; /** Angle of the module. */ public Rotation2d angle = Rotation2d.fromDegrees(0); - public static final SwerveModuleStateStruct struct = new SwerveModuleStateStruct(); + /** SwerveModuleState protobuf for serialization. */ public static final SwerveModuleStateProto proto = new SwerveModuleStateProto(); + /** SwerveModuleState struct for serialization. */ + public static final SwerveModuleStateStruct struct = new SwerveModuleStateStruct(); + /** Constructs a SwerveModuleState with zeros for speed and angle. */ public SwerveModuleState() {} diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/WheelPositions.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/WheelPositions.java index 029a0ac40d7..c0167e3e866 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/WheelPositions.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/WheelPositions.java @@ -6,6 +6,11 @@ import edu.wpi.first.math.interpolation.Interpolatable; +/** + * Interface for wheel positions. + * + * @param Wheel positions type. + */ public interface WheelPositions> extends Interpolatable { /** * Returns a copy of this instance. diff --git a/wpimath/src/main/java/edu/wpi/first/math/path/TravelingSalesman.java b/wpimath/src/main/java/edu/wpi/first/math/path/TravelingSalesman.java index 83dd93ef0bc..b81db2036bc 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/path/TravelingSalesman.java +++ b/wpimath/src/main/java/edu/wpi/first/math/path/TravelingSalesman.java @@ -62,7 +62,7 @@ public Pose2d[] solve(Pose2d[] poses, int iterations) { sum += m_cost.applyAsDouble( poses[(int) state.get(i, 0)], - poses[(int) (state.get((i + 1) % poses.length, 0))]); + poses[(int) state.get((i + 1) % poses.length, 0)]); } return sum; }); diff --git a/wpimath/src/main/java/edu/wpi/first/math/spline/CubicHermiteSpline.java b/wpimath/src/main/java/edu/wpi/first/math/spline/CubicHermiteSpline.java index 8f93661f995..72108a5deb5 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/spline/CubicHermiteSpline.java +++ b/wpimath/src/main/java/edu/wpi/first/math/spline/CubicHermiteSpline.java @@ -6,6 +6,7 @@ import org.ejml.simple.SimpleMatrix; +/** Represents a hermite spline of degree 3. */ public class CubicHermiteSpline extends Spline { private static SimpleMatrix hermiteBasis; private final SimpleMatrix m_coefficients; diff --git a/wpimath/src/main/java/edu/wpi/first/math/spline/PoseWithCurvature.java b/wpimath/src/main/java/edu/wpi/first/math/spline/PoseWithCurvature.java index 30b3caefef6..fbeeb35dc8c 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/spline/PoseWithCurvature.java +++ b/wpimath/src/main/java/edu/wpi/first/math/spline/PoseWithCurvature.java @@ -8,10 +8,10 @@ /** Represents a pair of a pose and a curvature. */ public class PoseWithCurvature { - // Represents the pose. + /** Represents the pose. */ public Pose2d poseMeters; - // Represents the curvature. + /** Represents the curvature. */ public double curvatureRadPerMeter; /** diff --git a/wpimath/src/main/java/edu/wpi/first/math/spline/QuinticHermiteSpline.java b/wpimath/src/main/java/edu/wpi/first/math/spline/QuinticHermiteSpline.java index 6d9ded02430..5d38bef60a7 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/spline/QuinticHermiteSpline.java +++ b/wpimath/src/main/java/edu/wpi/first/math/spline/QuinticHermiteSpline.java @@ -6,6 +6,7 @@ import org.ejml.simple.SimpleMatrix; +/** Represents a hermite spline of degree 5. */ public class QuinticHermiteSpline extends Spline { private static SimpleMatrix hermiteBasis; private final SimpleMatrix m_coefficients; diff --git a/wpimath/src/main/java/edu/wpi/first/math/spline/Spline.java b/wpimath/src/main/java/edu/wpi/first/math/spline/Spline.java index afdb2d22557..dc6c5e69969 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/spline/Spline.java +++ b/wpimath/src/main/java/edu/wpi/first/math/spline/Spline.java @@ -99,7 +99,10 @@ public PoseWithCurvature getPoint(double t) { * the value of x[2] is the second derivative in the x dimension. */ public static class ControlVector { + /** The x components of the control vector. */ public double[] x; + + /** The y components of the control vector. */ public double[] y; /** diff --git a/wpimath/src/main/java/edu/wpi/first/math/spline/SplineHelper.java b/wpimath/src/main/java/edu/wpi/first/math/spline/SplineHelper.java index d6a95671df0..3ba27144039 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/spline/SplineHelper.java +++ b/wpimath/src/main/java/edu/wpi/first/math/spline/SplineHelper.java @@ -10,6 +10,7 @@ import java.util.List; import org.ejml.simple.SimpleMatrix; +/** Helper class that is used to generate cubic and quintic splines from user provided waypoints. */ public final class SplineHelper { /** Private constructor because this is a utility class. */ private SplineHelper() {} diff --git a/wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java b/wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java index b2a34a5db5d..5cf742b3da4 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java +++ b/wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java @@ -56,6 +56,7 @@ private static class StackContents { } } + /** Exception for malformed splines. */ public static class MalformedSplineException extends RuntimeException { /** * Create a new exception with the given message. diff --git a/wpimath/src/main/java/edu/wpi/first/math/system/Discretization.java b/wpimath/src/main/java/edu/wpi/first/math/system/Discretization.java index b2fe9c8c8ec..b4afdf476af 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/system/Discretization.java +++ b/wpimath/src/main/java/edu/wpi/first/math/system/Discretization.java @@ -9,6 +9,7 @@ import edu.wpi.first.math.Pair; import org.ejml.simple.SimpleMatrix; +/** Discretization helper functions. */ public final class Discretization { private Discretization() { // Utility class diff --git a/wpimath/src/main/java/edu/wpi/first/math/system/LinearSystem.java b/wpimath/src/main/java/edu/wpi/first/math/system/LinearSystem.java index d7b660298ad..703498fe527 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/system/LinearSystem.java +++ b/wpimath/src/main/java/edu/wpi/first/math/system/LinearSystem.java @@ -8,6 +8,18 @@ import edu.wpi.first.math.Num; import edu.wpi.first.math.numbers.N1; +/** + * A plant defined using state-space notation. + * + *

A plant is a mathematical model of a system's dynamics. + * + *

For more on the underlying math, read + * https://file.tavsys.net/control/controls-engineering-in-frc.pdf. + * + * @param Number of states. + * @param Number of inputs. + * @param Number of outputs. + */ public class LinearSystem { /** Continuous system matrix. */ private final Matrix m_A; @@ -168,7 +180,7 @@ public Matrix calculateX( Matrix x, Matrix clampedU, double dtSeconds) { var discABpair = Discretization.discretizeAB(m_A, m_B, dtSeconds); - return (discABpair.getFirst().times(x)).plus(discABpair.getSecond().times(clampedU)); + return discABpair.getFirst().times(x).plus(discABpair.getSecond().times(clampedU)); } /** diff --git a/wpimath/src/main/java/edu/wpi/first/math/system/LinearSystemLoop.java b/wpimath/src/main/java/edu/wpi/first/math/system/LinearSystemLoop.java index 8bd6c15abf2..78cace9a21d 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/system/LinearSystemLoop.java +++ b/wpimath/src/main/java/edu/wpi/first/math/system/LinearSystemLoop.java @@ -27,6 +27,10 @@ * *

For more on the underlying math, read https://file.tavsys.net/control/controls-engineering-in-frc.pdf. + * + * @param Number of states. + * @param Number of inputs. + * @param Number of outputs. */ public class LinearSystemLoop { private final LinearQuadraticRegulator m_controller; @@ -288,7 +292,7 @@ public Matrix getError() { * @return The error at that index. */ public double getError(int index) { - return (getController().getR().minus(m_observer.getXhat())).get(index, 0); + return getController().getR().minus(m_observer.getXhat()).get(index, 0); } /** diff --git a/wpimath/src/main/java/edu/wpi/first/math/system/NumericalIntegration.java b/wpimath/src/main/java/edu/wpi/first/math/system/NumericalIntegration.java index e9b46fcbe1d..36d7bcb7a74 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/system/NumericalIntegration.java +++ b/wpimath/src/main/java/edu/wpi/first/math/system/NumericalIntegration.java @@ -11,6 +11,7 @@ import java.util.function.DoubleFunction; import java.util.function.Function; +/** Numerical integration utilities. */ public final class NumericalIntegration { private NumericalIntegration() { // utility Class diff --git a/wpimath/src/main/java/edu/wpi/first/math/system/NumericalJacobian.java b/wpimath/src/main/java/edu/wpi/first/math/system/NumericalJacobian.java index 43eba93bbe7..1be22a8fca5 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/system/NumericalJacobian.java +++ b/wpimath/src/main/java/edu/wpi/first/math/system/NumericalJacobian.java @@ -11,6 +11,7 @@ import java.util.function.BiFunction; import java.util.function.Function; +/** Numerical Jacobian utilities. */ public final class NumericalJacobian { private NumericalJacobian() { // Utility Class. diff --git a/wpimath/src/main/java/edu/wpi/first/math/system/plant/DCMotor.java b/wpimath/src/main/java/edu/wpi/first/math/system/plant/DCMotor.java index 9f5d67dbf0e..6d18846f254 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/system/plant/DCMotor.java +++ b/wpimath/src/main/java/edu/wpi/first/math/system/plant/DCMotor.java @@ -11,17 +11,35 @@ import edu.wpi.first.util.struct.StructSerializable; /** Holds the constants for a DC motor. */ -public class DCMotor implements ProtobufSerializable, StructSerializable { +public class DCMotor implements ProtobufSerializable, StructSerializable { + /** Voltage at which the motor constants were measured. */ public final double nominalVoltageVolts; + + /** Torque when stalled. */ public final double stallTorqueNewtonMeters; + + /** Current draw when stalled. */ public final double stallCurrentAmps; + + /** Current draw under no load. */ public final double freeCurrentAmps; + + /** Angular velocity under no load. */ public final double freeSpeedRadPerSec; + + /** Motor internal resistance. */ public final double rOhms; + + /** Motor velocity constant. */ public final double KvRadPerSecPerVolt; + + /** Motor torque constant. */ public final double KtNMPerAmp; + /** DCMotor protobuf for serialization. */ public static final DCMotorProto proto = new DCMotorProto(); + + /** DCMotor struct for serialization. */ public static final DCMotorStruct struct = new DCMotorStruct(); /** diff --git a/wpimath/src/main/java/edu/wpi/first/math/system/plant/LinearSystemId.java b/wpimath/src/main/java/edu/wpi/first/math/system/plant/LinearSystemId.java index 3ab305872db..2e7d4b94f22 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/system/plant/LinearSystemId.java +++ b/wpimath/src/main/java/edu/wpi/first/math/system/plant/LinearSystemId.java @@ -12,6 +12,7 @@ import edu.wpi.first.math.numbers.N2; import edu.wpi.first.math.system.LinearSystem; +/** Linear system ID utility functions. */ public final class LinearSystemId { private LinearSystemId() { // Utility class diff --git a/wpimath/src/main/java/edu/wpi/first/math/trajectory/ExponentialProfile.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/ExponentialProfile.java index 1580e853c77..e2dae9322a0 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/trajectory/ExponentialProfile.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/ExponentialProfile.java @@ -39,10 +39,20 @@ public class ExponentialProfile { private final Constraints m_constraints; + /** Profile timing. */ public static class ProfileTiming { + /** Profile inflection time. */ public final double inflectionTime; + + /** Total profile time. */ public final double totalTime; + /** + * Constructs a ProfileTiming. + * + * @param inflectionTime Profile inflection time. + * @param totalTime Total profile time. + */ protected ProfileTiming(double inflectionTime, double totalTime) { this.inflectionTime = inflectionTime; this.totalTime = totalTime; @@ -55,18 +65,23 @@ protected ProfileTiming(double inflectionTime, double totalTime) { * @return if the profile is finished at time t. */ public boolean isFinished(double t) { - return t > inflectionTime; + return t >= inflectionTime; } } + /** Profile constraints. */ public static class Constraints { + /** Maximum unsigned input voltage. */ public final double maxInput; + /** The State-Space 1x1 system matrix. */ public final double A; + + /** The State-Space 1x1 input matrix. */ public final double B; /** - * Construct constraints for an ExponentialProfile. + * Constructs constraints for an ExponentialProfile. * * @param maxInput maximum unsigned input voltage * @param A The State-Space 1x1 system matrix. @@ -88,7 +103,7 @@ public double maxVelocity() { } /** - * Construct constraints for an ExponentialProfile from characteristics. + * Constructs constraints for an ExponentialProfile from characteristics. * * @param maxInput maximum unsigned input voltage * @param kV The velocity gain. @@ -100,7 +115,7 @@ public static Constraints fromCharacteristics(double maxInput, double kV, double } /** - * Construct constraints for an ExponentialProfile from State-Space parameters. + * Constructs constraints for an ExponentialProfile from State-Space parameters. * * @param maxInput maximum unsigned input voltage * @param A The State-Space 1x1 system matrix. @@ -112,13 +127,19 @@ public static Constraints fromStateSpace(double maxInput, double A, double B) { } } + /** Profile state. */ public static class State { - public final double position; + /** The position at this state. */ + public double position; + + /** The velocity at this state. */ + public double velocity; - public final double velocity; + /** Default constructor. */ + public State() {} /** - * Construct a state within an exponential profile. + * Constructs a state within an exponential profile. * * @param position The position at this state. * @param velocity The velocity at this state. @@ -145,7 +166,7 @@ public int hashCode() { } /** - * Construct an ExponentialProfile. + * Constructs an ExponentialProfile. * * @param constraints The constraints on the profile, like maximum input. */ @@ -154,10 +175,10 @@ public ExponentialProfile(Constraints constraints) { } /** - * Calculate the correct position and velocity for the profile at a time t where the current state - * is at time t = 0. + * Calculates the position and velocity for the profile at a time t where the current state is at + * time t = 0. * - * @param t The time since the beginning of the profile. + * @param t How long to advance from the current state toward the desired state. * @param current The current state. * @param goal The desired state when the profile is complete. * @return The position and velocity of the profile at time t. @@ -184,7 +205,7 @@ public State calculate(double t, State current, State goal) { } /** - * Calculate the point after which the fastest way to reach the goal state is to apply input in + * Calculates the point after which the fastest way to reach the goal state is to apply input in * the opposite direction. * * @param current The current state. @@ -199,7 +220,7 @@ public State calculateInflectionPoint(State current, State goal) { } /** - * Calculate the point after which the fastest way to reach the goal state is to apply input in + * Calculates the point after which the fastest way to reach the goal state is to apply input in * the opposite direction. * * @param current The current state. @@ -221,7 +242,7 @@ private State calculateInflectionPoint(State current, State goal, double input) } /** - * Calculate the time it will take for this profile to reach the goal state. + * Calculates the time it will take for this profile to reach the goal state. * * @param current The current state. * @param goal The desired state when the profile is complete. @@ -234,8 +255,8 @@ public double timeLeftUntil(State current, State goal) { } /** - * Calculate the time it will take for this profile to reach the inflection point, and the time it - * will take for this profile to reach the goal state. + * Calculates the time it will take for this profile to reach the inflection point, and the time + * it will take for this profile to reach the goal state. * * @param current The current state. * @param goal The desired state when the profile is complete. @@ -250,8 +271,8 @@ public ProfileTiming calculateProfileTiming(State current, State goal) { } /** - * Calculate the time it will take for this profile to reach the inflection point, and the time it - * will take for this profile to reach the goal state. + * Calculates the time it will take for this profile to reach the inflection point, and the time + * it will take for this profile to reach the goal state. * * @param current The current state. * @param inflectionPoint The inflection point of this profile. @@ -311,7 +332,7 @@ private ProfileTiming calculateProfileTiming( } /** - * Calculate the position reached after t seconds when applying an input from the initial state. + * Calculates the position reached after t seconds when applying an input from the initial state. * * @param t The time since the initial state. * @param input The signed input applied to this profile from the initial state. @@ -328,7 +349,7 @@ private double computeDistanceFromTime(double t, double input, State initial) { } /** - * Calculate the velocity reached after t seconds when applying an input from the initial state. + * Calculates the velocity reached after t seconds when applying an input from the initial state. * * @param t The time since the initial state. * @param input The signed input applied to this profile from the initial state. @@ -344,7 +365,7 @@ private double computeVelocityFromTime(double t, double input, State initial) { } /** - * Calculate the time required to reach a specified velocity given the initial velocity. + * Calculates the time required to reach a specified velocity given the initial velocity. * * @param velocity The goal velocity. * @param input The signed input applied to this profile from the initial state. @@ -360,7 +381,7 @@ private double computeTimeFromVelocity(double velocity, double input, double ini } /** - * Calculate the distance reached at the same time as the given velocity when applying the given + * Calculates the distance reached at the same time as the given velocity when applying the given * input from the initial state. * * @param velocity The velocity reached by this profile @@ -379,7 +400,7 @@ private double computeDistanceFromVelocity(double velocity, double input, State } /** - * Calculate the velocity at which input should be reversed in order to reach the goal state from + * Calculates the velocity at which input should be reversed in order to reach the goal state from * the current state. * * @param input The signed input applied to this profile from the current state. diff --git a/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java index ff54157bdb2..c0dc037a758 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java @@ -19,7 +19,8 @@ * Represents a time-parameterized trajectory. The trajectory contains of various States that * represent the pose, curvature, time elapsed, velocity, and acceleration at that point. */ -public class Trajectory implements ProtobufSerializable { +public class Trajectory implements ProtobufSerializable { + /** Trajectory protobuf for serialization. */ public static final TrajectoryProto proto = new TrajectoryProto(); private final double m_totalTimeSeconds; @@ -268,29 +269,31 @@ public Trajectory concatenate(Trajectory other) { * Represents a time-parameterized trajectory. The trajectory contains of various States that * represent the pose, curvature, time elapsed, velocity, and acceleration at that point. */ - public static class State implements ProtobufSerializable { + public static class State implements ProtobufSerializable { + /** Trajectory.State protobuf for serialization. */ public static final TrajectoryStateProto proto = new TrajectoryStateProto(); - // The time elapsed since the beginning of the trajectory. + /** The time elapsed since the beginning of the trajectory. */ @JsonProperty("time") public double timeSeconds; - // The speed at that point of the trajectory. + /** The speed at that point of the trajectory. */ @JsonProperty("velocity") public double velocityMetersPerSecond; - // The acceleration at that point of the trajectory. + /** The acceleration at that point of the trajectory. */ @JsonProperty("acceleration") public double accelerationMetersPerSecondSq; - // The pose at that point of the trajectory. + /** The pose at that point of the trajectory. */ @JsonProperty("pose") public Pose2d poseMeters; - // The curvature at that point of the trajectory. + /** The curvature at that point of the trajectory. */ @JsonProperty("curvature") public double curvatureRadPerMeter; + /** Default constructor. */ public State() { poseMeters = new Pose2d(); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryGenerator.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryGenerator.java index e87070292cf..fcfef4508c4 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryGenerator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryGenerator.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.function.BiConsumer; +/** Helper class used to generate trajectories with various constraints. */ public final class TrajectoryGenerator { private static final Trajectory kDoNothingTrajectory = new Trajectory(List.of(new Trajectory.State())); @@ -263,16 +264,27 @@ public static List splinePointsFromSplines(Spline[] splines) return splinePoints; } - // Work around type erasure signatures + /** Control vector list type that works around type erasure signatures. */ public static class ControlVectorList extends ArrayList { - public ControlVectorList(int initialCapacity) { - super(initialCapacity); - } - + /** Default constructor. */ public ControlVectorList() { super(); } + /** + * Constructs a ControlVectorList. + * + * @param initialCapacity The initial list capacity. + */ + public ControlVectorList(int initialCapacity) { + super(initialCapacity); + } + + /** + * Constructs a ControlVectorList. + * + * @param collection A collection of spline control vectors. + */ public ControlVectorList(Collection collection) { super(collection); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java index a741aa09db2..35b2a465056 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java @@ -315,7 +315,13 @@ private static class ConstrainedState { } } + /** Exception for trajectory generation failure. */ public static class TrajectoryGenerationException extends RuntimeException { + /** + * Constructs a TrajectoryGenerationException. + * + * @param message Exception message. + */ public TrajectoryGenerationException(String message) { super(message); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryUtil.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryUtil.java index 2cbba908615..f1b2d6a97b6 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryUtil.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryUtil.java @@ -12,6 +12,7 @@ import java.util.ArrayList; import java.util.List; +/** Trajectory utilities. */ public final class TrajectoryUtil { private TrajectoryUtil() { throw new UnsupportedOperationException("This is a utility class!"); @@ -111,7 +112,13 @@ public static String serializeTrajectory(Trajectory trajectory) { return WPIMathJNI.serializeTrajectory(getElementsFromTrajectory(trajectory)); } + /** Exception for trajectory serialization failure. */ public static class TrajectorySerializationException extends RuntimeException { + /** + * Constructs a TrajectorySerializationException. + * + * @param message The exception message. + */ public TrajectorySerializationException(String message) { super(message); } diff --git a/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrapezoidProfile.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrapezoidProfile.java index f14aea78f0a..657ea24fe3b 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrapezoidProfile.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrapezoidProfile.java @@ -54,13 +54,16 @@ public class TrapezoidProfile { private double m_endFullSpeed; private double m_endDeccel; + /** Profile constraints. */ public static class Constraints { + /** Maximum velocity. */ public final double maxVelocity; + /** Maximum acceleration. */ public final double maxAcceleration; /** - * Construct constraints for a TrapezoidProfile. + * Constructs constraints for a TrapezoidProfile. * * @param maxVelocity maximum velocity * @param maxAcceleration maximum acceleration @@ -71,24 +74,48 @@ public Constraints(double maxVelocity, double maxAcceleration) { MathSharedStore.reportUsage(MathUsageId.kTrajectory_TrapezoidProfile, 1); } + /** + * Constructs constraints for a TrapezoidProfile. + * + * @param Unit type. + * @param maxVelocity maximum velocity + * @param maxAcceleration maximum acceleration + */ public > Constraints( Measure> maxVelocity, Measure>> maxAcceleration) { this(maxVelocity.baseUnitMagnitude(), maxAcceleration.baseUnitMagnitude()); } } + /** Profile state. */ public static class State { + /** The position at this state. */ public double position; + /** The velocity at this state. */ public double velocity; + /** Default constructor. */ public State() {} + /** + * Constructs constraints for a Trapezoid Profile. + * + * @param position The position at this state. + * @param velocity The velocity at this state. + */ public State(double position, double velocity) { this.position = position; this.velocity = velocity; } + /** + * Constructs constraints for a Trapezoid Profile. + * + * @param Unit type. + * @param position The position at this state. + * @param velocity The velocity at this state. + */ public > State(Measure position, Measure> velocity) { this(position.baseUnitMagnitude(), velocity.baseUnitMagnitude()); } @@ -110,7 +137,7 @@ public int hashCode() { } /** - * Construct a TrapezoidProfile. + * Constructs a TrapezoidProfile. * * @param constraints The constraints on the profile, like maximum velocity. */ @@ -120,7 +147,7 @@ public TrapezoidProfile(Constraints constraints) { } /** - * Construct a TrapezoidProfile. + * Constructs a TrapezoidProfile. * * @param constraints The constraints on the profile, like maximum velocity. * @param goal The desired state when the profile is complete. @@ -170,7 +197,7 @@ public TrapezoidProfile(Constraints constraints, State goal, State initial) { } /** - * Construct a TrapezoidProfile. + * Constructs a TrapezoidProfile. * * @param constraints The constraints on the profile, like maximum velocity. * @param goal The desired state when the profile is complete. @@ -183,10 +210,10 @@ public TrapezoidProfile(Constraints constraints, State goal) { } /** - * Calculate the correct position and velocity for the profile at a time t where the beginning of - * the profile was at time t = 0. + * Calculates the position and velocity for the profile at a time t where the current state is at + * time t = 0. * - * @param t The time since the beginning of the profile. + * @param t How long to advance from the current state toward the desired state. * @return The position and velocity of the profile at time t. * @deprecated Pass the desired and current state into calculate instead of constructing a new * TrapezoidProfile with the desired and current state @@ -220,10 +247,10 @@ public State calculate(double t) { } /** - * Calculate the correct position and velocity for the profile at a time t where the beginning of - * the profile was at time t = 0. + * Calculates the position and velocity for the profile at a time t where the current state is at + * time t = 0. * - * @param t The time since the beginning of the profile. + * @param t How long to advance from the current state toward the desired state. * @param current The current state. * @param goal The desired state when the profile is complete. * @return The position and velocity of the profile at time t. diff --git a/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/TrajectoryConstraint.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/TrajectoryConstraint.java index 1f2c9cfb597..48490d4eba5 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/TrajectoryConstraint.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/TrajectoryConstraint.java @@ -37,7 +37,10 @@ MinMax getMinMaxAccelerationMetersPerSecondSq( /** Represents a minimum and maximum acceleration. */ class MinMax { + /** The minimum acceleration. */ public double minAccelerationMetersPerSecondSq = -Double.MAX_VALUE; + + /** The maximum acceleration. */ public double maxAccelerationMetersPerSecondSq = Double.MAX_VALUE; /** diff --git a/wpimath/src/main/native/cpp/geometry/Quaternion.cpp b/wpimath/src/main/native/cpp/geometry/Quaternion.cpp index 3aad64c377e..ea9b179ca82 100644 --- a/wpimath/src/main/native/cpp/geometry/Quaternion.cpp +++ b/wpimath/src/main/native/cpp/geometry/Quaternion.cpp @@ -8,8 +8,6 @@ #include -#include "geometry3d.pb.h" - using namespace frc; Quaternion::Quaternion(double w, double x, double y, double z) diff --git a/wpimath/src/main/native/cpp/kinematics/proto/MecanumDriveKinematicsProto.cpp b/wpimath/src/main/native/cpp/kinematics/proto/MecanumDriveKinematicsProto.cpp index 5b3c027e6ef..e17102ffbfd 100644 --- a/wpimath/src/main/native/cpp/kinematics/proto/MecanumDriveKinematicsProto.cpp +++ b/wpimath/src/main/native/cpp/kinematics/proto/MecanumDriveKinematicsProto.cpp @@ -26,8 +26,8 @@ frc::MecanumDriveKinematics wpi::Protobuf::Unpack( void wpi::Protobuf::Pack( google::protobuf::Message* msg, const frc::MecanumDriveKinematics& value) { auto m = static_cast(msg); - wpi::PackProtobuf(m->mutable_front_left(), value.GetFrontLeftWheel()); - wpi::PackProtobuf(m->mutable_front_right(), value.GetFrontRightWheel()); - wpi::PackProtobuf(m->mutable_rear_left(), value.GetRearLeftWheel()); - wpi::PackProtobuf(m->mutable_rear_right(), value.GetRearRightWheel()); + wpi::PackProtobuf(m->mutable_front_left(), value.GetFrontLeft()); + wpi::PackProtobuf(m->mutable_front_right(), value.GetFrontRight()); + wpi::PackProtobuf(m->mutable_rear_left(), value.GetRearLeft()); + wpi::PackProtobuf(m->mutable_rear_right(), value.GetRearRight()); } diff --git a/wpimath/src/main/native/cpp/kinematics/struct/MecanumDriveKinematicsStruct.cpp b/wpimath/src/main/native/cpp/kinematics/struct/MecanumDriveKinematicsStruct.cpp index 261966cee16..1a27a286a6e 100644 --- a/wpimath/src/main/native/cpp/kinematics/struct/MecanumDriveKinematicsStruct.cpp +++ b/wpimath/src/main/native/cpp/kinematics/struct/MecanumDriveKinematicsStruct.cpp @@ -27,8 +27,8 @@ frc::MecanumDriveKinematics StructType::Unpack(std::span data) { void StructType::Pack(std::span data, const frc::MecanumDriveKinematics& value) { - wpi::PackStruct(data, value.GetFrontLeftWheel()); - wpi::PackStruct(data, value.GetFrontRightWheel()); - wpi::PackStruct(data, value.GetRearLeftWheel()); - wpi::PackStruct(data, value.GetRearRightWheel()); + wpi::PackStruct(data, value.GetFrontLeft()); + wpi::PackStruct(data, value.GetFrontRight()); + wpi::PackStruct(data, value.GetRearLeft()); + wpi::PackStruct(data, value.GetRearRight()); } diff --git a/wpimath/src/main/native/include/frc/StateSpaceUtil.h b/wpimath/src/main/native/include/frc/StateSpaceUtil.h index e2b638ca24b..65bed6fb1d4 100644 --- a/wpimath/src/main/native/include/frc/StateSpaceUtil.h +++ b/wpimath/src/main/native/include/frc/StateSpaceUtil.h @@ -194,8 +194,8 @@ Eigen::Vector4d PoseTo4dVector(const Pose2d& pose); * any, have absolute values less than one, where an eigenvalue is * uncontrollable if rank([λI - A, B]) < n where n is the number of states. * - * @tparam States The number of states. - * @tparam Inputs The number of inputs. + * @tparam States Number of states. + * @tparam Inputs Number of inputs. * @param A System matrix. * @param B Input matrix. */ @@ -253,8 +253,8 @@ IsStabilizable(const Eigen::MatrixXd& A, * any, have absolute values less than one, where an eigenvalue is unobservable * if rank([λI - A; C]) < n where n is the number of states. * - * @tparam States The number of states. - * @tparam Outputs The number of outputs. + * @tparam States Number of states. + * @tparam Outputs Number of outputs. * @param A System matrix. * @param C Output matrix. */ @@ -277,7 +277,7 @@ Eigen::Vector3d PoseToVector(const Pose2d& pose); /** * Clamps input vector between system's minimum and maximum allowable input. * - * @tparam Inputs The number of inputs. + * @tparam Inputs Number of inputs. * @param u Input vector to clamp. * @param umin The minimum input magnitude. * @param umax The maximum input magnitude. @@ -298,7 +298,7 @@ Vectord ClampInputMaxMagnitude(const Vectord& u, * Renormalize all inputs if any exceeds the maximum magnitude. Useful for * systems such as differential drivetrains. * - * @tparam Inputs The number of inputs. + * @tparam Inputs Number of inputs. * @param u The input vector. * @param maxMagnitude The maximum magnitude any input can have. * @return The normalizedInput diff --git a/wpimath/src/main/native/include/frc/controller/ArmFeedforward.h b/wpimath/src/main/native/include/frc/controller/ArmFeedforward.h index 933a4051d63..69091ccd9a3 100644 --- a/wpimath/src/main/native/include/frc/controller/ArmFeedforward.h +++ b/wpimath/src/main/native/include/frc/controller/ArmFeedforward.h @@ -33,10 +33,10 @@ class WPILIB_DLLEXPORT ArmFeedforward { /** * Creates a new ArmFeedforward with the specified gains. * - * @param kS The static gain, in volts. + * @param kS The static gain, in volts. * @param kG The gravity gain, in volts. - * @param kV The velocity gain, in volt seconds per radian. - * @param kA The acceleration gain, in volt seconds² per radian. + * @param kV The velocity gain, in volt seconds per radian. + * @param kA The acceleration gain, in volt seconds² per radian. */ constexpr ArmFeedforward( units::volt_t kS, units::volt_t kG, units::unit_t kV, @@ -175,9 +175,16 @@ class WPILIB_DLLEXPORT ArmFeedforward { return MaxAchievableAcceleration(-maxVoltage, angle, velocity); } + /// The static gain, in volts. const units::volt_t kS; + + /// The gravity gain, in volts. const units::volt_t kG; + + /// The velocity gain, in volt seconds per radian. const units::unit_t kV; + + /// The acceleration gain, in volt seconds² per radian. const units::unit_t kA; }; } // namespace frc diff --git a/wpimath/src/main/native/include/frc/controller/ControlAffinePlantInversionFeedforward.h b/wpimath/src/main/native/include/frc/controller/ControlAffinePlantInversionFeedforward.h index 63064576557..c3c282d3e63 100644 --- a/wpimath/src/main/native/include/frc/controller/ControlAffinePlantInversionFeedforward.h +++ b/wpimath/src/main/native/include/frc/controller/ControlAffinePlantInversionFeedforward.h @@ -34,8 +34,8 @@ namespace frc { * For more on the underlying math, read * https://file.tavsys.net/control/controls-engineering-in-frc.pdf. * - * @tparam States The number of states. - * @tparam Inputs the number of inputs. + * @tparam States Number of states. + * @tparam Inputs Number of inputs. */ template class ControlAffinePlantInversionFeedforward { diff --git a/wpimath/src/main/native/include/frc/controller/DifferentialDriveWheelVoltages.h b/wpimath/src/main/native/include/frc/controller/DifferentialDriveWheelVoltages.h index 235a95877e8..424cd2d1b39 100644 --- a/wpimath/src/main/native/include/frc/controller/DifferentialDriveWheelVoltages.h +++ b/wpimath/src/main/native/include/frc/controller/DifferentialDriveWheelVoltages.h @@ -12,7 +12,10 @@ namespace frc { * Motor voltages for a differential drive. */ struct DifferentialDriveWheelVoltages { + /// Left wheel voltage. units::volt_t left = 0_V; + + /// Right wheel voltage. units::volt_t right = 0_V; }; diff --git a/wpimath/src/main/native/include/frc/controller/ElevatorFeedforward.h b/wpimath/src/main/native/include/frc/controller/ElevatorFeedforward.h index 2d81a0ae333..07a0499a53c 100644 --- a/wpimath/src/main/native/include/frc/controller/ElevatorFeedforward.h +++ b/wpimath/src/main/native/include/frc/controller/ElevatorFeedforward.h @@ -182,9 +182,16 @@ class ElevatorFeedforward { return MaxAchievableAcceleration(-maxVoltage, velocity); } + /// The static gain. const units::volt_t kS; + + /// The gravity gain. const units::volt_t kG; + + /// The velocity gain. const units::unit_t kV; + + /// The acceleration gain. const units::unit_t kA; }; } // namespace frc diff --git a/wpimath/src/main/native/include/frc/controller/ImplicitModelFollower.h b/wpimath/src/main/native/include/frc/controller/ImplicitModelFollower.h index 3a1230d4bcf..f030d4898c9 100644 --- a/wpimath/src/main/native/include/frc/controller/ImplicitModelFollower.h +++ b/wpimath/src/main/native/include/frc/controller/ImplicitModelFollower.h @@ -24,6 +24,9 @@ namespace frc { * * For more on the underlying math, read appendix B.3 in * https://file.tavsys.net/control/controls-engineering-in-frc.pdf. + * + * @tparam States Number of states. + * @tparam Inputs Number of inputs. */ template class ImplicitModelFollower { diff --git a/wpimath/src/main/native/include/frc/controller/LinearPlantInversionFeedforward.h b/wpimath/src/main/native/include/frc/controller/LinearPlantInversionFeedforward.h index 1d905e24ab2..fb46b030926 100644 --- a/wpimath/src/main/native/include/frc/controller/LinearPlantInversionFeedforward.h +++ b/wpimath/src/main/native/include/frc/controller/LinearPlantInversionFeedforward.h @@ -26,8 +26,8 @@ namespace frc { * For more on the underlying math, read * https://file.tavsys.net/control/controls-engineering-in-frc.pdf. * - * @tparam States The number of states. - * @tparam Inputs The number of inputs. + * @tparam States Number of states. + * @tparam Inputs Number of inputs. */ template class LinearPlantInversionFeedforward { @@ -38,7 +38,7 @@ class LinearPlantInversionFeedforward { /** * Constructs a feedforward with the given plant. * - * @tparam Outputs The number of outputs. + * @tparam Outputs Number of outputs. * @param plant The plant being controlled. * @param dt Discretization timestep. */ diff --git a/wpimath/src/main/native/include/frc/controller/LinearQuadraticRegulator.h b/wpimath/src/main/native/include/frc/controller/LinearQuadraticRegulator.h index 979e98a371f..8389a88a642 100644 --- a/wpimath/src/main/native/include/frc/controller/LinearQuadraticRegulator.h +++ b/wpimath/src/main/native/include/frc/controller/LinearQuadraticRegulator.h @@ -40,6 +40,7 @@ class LinearQuadraticRegulator { * https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-intro.html#lqr-tuning * for how to select the tolerances. * + * @tparam Outputs Number of outputs. * @param plant The plant being controlled. * @param Qelems The maximum desired error tolerance for each state. * @param Relems The maximum desired control effort for each input. diff --git a/wpimath/src/main/native/include/frc/controller/SimpleMotorFeedforward.h b/wpimath/src/main/native/include/frc/controller/SimpleMotorFeedforward.h index 767b05d4d69..cc31fc10582 100644 --- a/wpimath/src/main/native/include/frc/controller/SimpleMotorFeedforward.h +++ b/wpimath/src/main/native/include/frc/controller/SimpleMotorFeedforward.h @@ -160,8 +160,13 @@ class SimpleMotorFeedforward { return MaxAchievableAcceleration(-maxVoltage, velocity); } + /** The static gain. */ const units::volt_t kS; + + /** The velocity gain. */ const units::unit_t kV; + + /** The acceleration gain. */ const units::unit_t kA; }; } // namespace frc diff --git a/wpimath/src/main/native/include/frc/estimator/AngleStatistics.h b/wpimath/src/main/native/include/frc/estimator/AngleStatistics.h index 026cc678812..de0de37e960 100644 --- a/wpimath/src/main/native/include/frc/estimator/AngleStatistics.h +++ b/wpimath/src/main/native/include/frc/estimator/AngleStatistics.h @@ -16,7 +16,7 @@ namespace frc { * Subtracts a and b while normalizing the resulting value in the selected row * as if it were an angle. * - * @tparam States The number of states. + * @tparam States Number of states. * @param a A vector to subtract from. * @param b A vector to subtract with. * @param angleStateIdx The row containing angles to be normalized. @@ -34,7 +34,7 @@ Vectord AngleResidual(const Vectord& a, * Returns a function that subtracts two vectors while normalizing the resulting * value in the selected row as if it were an angle. * - * @tparam States The number of states. + * @tparam States Number of states. * @param angleStateIdx The row containing angles to be normalized. */ template @@ -49,7 +49,7 @@ AngleResidual(int angleStateIdx) { * Adds a and b while normalizing the resulting value in the selected row as an * angle. * - * @tparam States The number of states. + * @tparam States Number of states. * @param a A vector to add with. * @param b A vector to add with. * @param angleStateIdx The row containing angles to be normalized. @@ -67,7 +67,7 @@ Vectord AngleAdd(const Vectord& a, const Vectord& b, * Returns a function that adds two vectors while normalizing the resulting * value in the selected row as an angle. * - * @tparam States The number of states. + * @tparam States Number of states. * @param angleStateIdx The row containing angles to be normalized. */ template @@ -82,7 +82,7 @@ AngleAdd(int angleStateIdx) { * * @tparam CovDim Dimension of covariance of sigma points after passing through * the transform. - * @tparam States The number of states. + * @tparam States Number of states. * @param sigmas Sigma points. * @param Wm Weights for the mean. * @param angleStatesIdx The row containing the angles. @@ -113,7 +113,7 @@ Vectord AngleMean(const Matrixd& sigmas, * * @tparam CovDim Dimension of covariance of sigma points after passing through * the transform. - * @tparam States The number of states. + * @tparam States Number of states. * @param angleStateIdx The row containing the angles. */ template diff --git a/wpimath/src/main/native/include/frc/estimator/ExtendedKalmanFilter.h b/wpimath/src/main/native/include/frc/estimator/ExtendedKalmanFilter.h index 32ed558f71b..68eb75b8818 100644 --- a/wpimath/src/main/native/include/frc/estimator/ExtendedKalmanFilter.h +++ b/wpimath/src/main/native/include/frc/estimator/ExtendedKalmanFilter.h @@ -33,9 +33,9 @@ namespace frc { * https://file.tavsys.net/control/controls-engineering-in-frc.pdf chapter 9 * "Stochastic control theory". * - * @tparam States The number of states. - * @tparam Inputs The number of inputs. - * @tparam Outputs The number of outputs. + * @tparam States Number of states. + * @tparam Inputs Number of inputs. + * @tparam Outputs Number of outputs. */ template class ExtendedKalmanFilter { diff --git a/wpimath/src/main/native/include/frc/estimator/KalmanFilter.h b/wpimath/src/main/native/include/frc/estimator/KalmanFilter.h index 3f9443d72b7..56dce0c11ca 100644 --- a/wpimath/src/main/native/include/frc/estimator/KalmanFilter.h +++ b/wpimath/src/main/native/include/frc/estimator/KalmanFilter.h @@ -28,9 +28,9 @@ namespace frc { * https://file.tavsys.net/control/controls-engineering-in-frc.pdf chapter 9 * "Stochastic control theory". * - * @tparam States The number of states. - * @tparam Inputs The number of inputs. - * @tparam Outputs The number of outputs. + * @tparam States Number of states. + * @tparam Inputs Number of inputs. + * @tparam Outputs Number of outputs. */ template class KalmanFilter { diff --git a/wpimath/src/main/native/include/frc/estimator/KalmanFilterLatencyCompensator.h b/wpimath/src/main/native/include/frc/estimator/KalmanFilterLatencyCompensator.h index d6e41271c8b..b93d614f016 100644 --- a/wpimath/src/main/native/include/frc/estimator/KalmanFilterLatencyCompensator.h +++ b/wpimath/src/main/native/include/frc/estimator/KalmanFilterLatencyCompensator.h @@ -16,13 +16,28 @@ namespace frc { +/** + * This class incorporates time-delayed measurements into a Kalman filter's + * state estimate. + * + * @tparam States The number of states. + * @tparam Inputs The number of inputs. + * @tparam Outputs The number of outputs. + */ template class KalmanFilterLatencyCompensator { public: + /** + * This class contains all the information about our observer at a given time. + */ struct ObserverSnapshot { + /// The state estimate. Vectord xHat; + /// The square root error covariance. Matrixd squareRootErrorCovariances; + /// The inputs. Vectord inputs; + /// The local measurements. Vectord localMeasurements; ObserverSnapshot(const KalmanFilterType& observer, const Vectord& u, diff --git a/wpimath/src/main/native/include/frc/estimator/MerweScaledSigmaPoints.h b/wpimath/src/main/native/include/frc/estimator/MerweScaledSigmaPoints.h index b2e0e458ca3..6395d8d3f1e 100644 --- a/wpimath/src/main/native/include/frc/estimator/MerweScaledSigmaPoints.h +++ b/wpimath/src/main/native/include/frc/estimator/MerweScaledSigmaPoints.h @@ -21,8 +21,8 @@ namespace frc { * [1] R. Van der Merwe "Sigma-Point Kalman Filters for Probabilitic * Inference in Dynamic State-Space Models" (Doctoral dissertation) * - * @tparam States The dimensionality of the state. 2*States+1 weights will be - * generated. + * @tparam States The dimensionality of the state. 2 * States + 1 weights will + * be generated. */ template class MerweScaledSigmaPoints { diff --git a/wpimath/src/main/native/include/frc/estimator/PoseEstimator.h b/wpimath/src/main/native/include/frc/estimator/PoseEstimator.h index eb437a40f7e..6e38d8c65fb 100644 --- a/wpimath/src/main/native/include/frc/estimator/PoseEstimator.h +++ b/wpimath/src/main/native/include/frc/estimator/PoseEstimator.h @@ -30,6 +30,9 @@ namespace frc { * * AddVisionMeasurement() can be called as infrequently as you want; if you * never call it, then this class will behave like regular encoder odometry. + * + * @tparam WheelSpeeds Wheel speeds type. + * @tparam WheelPositions Wheel positions type. */ template class WPILIB_DLLEXPORT PoseEstimator { diff --git a/wpimath/src/main/native/include/frc/estimator/SteadyStateKalmanFilter.h b/wpimath/src/main/native/include/frc/estimator/SteadyStateKalmanFilter.h index 6548b45660f..64b1782b97c 100644 --- a/wpimath/src/main/native/include/frc/estimator/SteadyStateKalmanFilter.h +++ b/wpimath/src/main/native/include/frc/estimator/SteadyStateKalmanFilter.h @@ -33,9 +33,9 @@ namespace frc { * https://file.tavsys.net/control/controls-engineering-in-frc.pdf chapter 9 * "Stochastic control theory". * - * @tparam States The number of states. - * @tparam Inputs The number of inputs. - * @tparam Outputs The number of outputs. + * @tparam States Number of states. + * @tparam Inputs Number of inputs. + * @tparam Outputs Number of outputs. */ template class SteadyStateKalmanFilter { diff --git a/wpimath/src/main/native/include/frc/estimator/UnscentedKalmanFilter.h b/wpimath/src/main/native/include/frc/estimator/UnscentedKalmanFilter.h index 9526f0cb489..df68e151606 100644 --- a/wpimath/src/main/native/include/frc/estimator/UnscentedKalmanFilter.h +++ b/wpimath/src/main/native/include/frc/estimator/UnscentedKalmanFilter.h @@ -39,9 +39,9 @@ namespace frc { * (SR-UKF). For more information about the SR-UKF, see * https://www.researchgate.net/publication/3908304. * - * @tparam States The number of states. - * @tparam Inputs The number of inputs. - * @tparam Outputs The number of outputs. + * @tparam States Number of states. + * @tparam Inputs Number of inputs. + * @tparam Outputs Number of outputs. */ template class UnscentedKalmanFilter { diff --git a/wpimath/src/main/native/include/frc/filter/Debouncer.h b/wpimath/src/main/native/include/frc/filter/Debouncer.h index a929f37358a..9ab3e0e1680 100644 --- a/wpimath/src/main/native/include/frc/filter/Debouncer.h +++ b/wpimath/src/main/native/include/frc/filter/Debouncer.h @@ -17,7 +17,17 @@ namespace frc { */ class WPILIB_DLLEXPORT Debouncer { public: - enum DebounceType { kRising, kFalling, kBoth }; + /** + * Type of debouncing to perform. + */ + enum DebounceType { + /// Rising edge. + kRising, + /// Falling edge. + kFalling, + /// Both rising and falling edges. + kBoth + }; /** * Creates a new Debouncer. diff --git a/wpimath/src/main/native/include/frc/filter/LinearFilter.h b/wpimath/src/main/native/include/frc/filter/LinearFilter.h index fc558dfd588..051a6ddd268 100644 --- a/wpimath/src/main/native/include/frc/filter/LinearFilter.h +++ b/wpimath/src/main/native/include/frc/filter/LinearFilter.h @@ -271,6 +271,75 @@ class LinearFilter { std::fill(m_outputs.begin(), m_outputs.end(), T{0.0}); } + /** + * Resets the filter state, initializing internal buffers to the provided + * values. + * + * These are the expected lengths of the buffers, depending on what type of + * linear filter used: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TypeInput Buffer SizeOutput Buffer Size
Unspecifiedsize of {@code ffGains}size of {@code fbGains}
Single Pole IIR11
High-Pass21
Moving Average{@code taps}0
Finite Differencesize of {@code stencil}0
Backward Finite Difference{@code Samples}0
+ * + * @param inputBuffer Values to initialize input buffer. + * @param outputBuffer Values to initialize output buffer. + * @throws std::runtime_error if size of inputBuffer or outputBuffer does not + * match the size of ffGains and fbGains provided in the constructor. + */ + void Reset(std::span inputBuffer, + std::span outputBuffer) { + // Clear buffers + Reset(); + + if (inputBuffer.size() != m_inputGains.size() || + outputBuffer.size() != m_outputGains.size()) { + throw std::runtime_error( + "Incorrect length of inputBuffer or outputBuffer"); + } + + for (size_t i = 0; i < inputBuffer.size(); ++i) { + m_inputs.push_front(inputBuffer[i]); + } + for (size_t i = 0; i < outputBuffer.size(); ++i) { + m_outputs.push_front(outputBuffer[i]); + } + } + /** * Calculates the next value of the filter. * diff --git a/wpimath/src/main/native/include/frc/geometry/Quaternion.h b/wpimath/src/main/native/include/frc/geometry/Quaternion.h index a9526bc3791..4fe694cf25e 100644 --- a/wpimath/src/main/native/include/frc/geometry/Quaternion.h +++ b/wpimath/src/main/native/include/frc/geometry/Quaternion.h @@ -10,6 +10,9 @@ namespace frc { +/** + * Represents a quaternion. + */ class WPILIB_DLLEXPORT Quaternion { public: /** diff --git a/wpimath/src/main/native/include/frc/geometry/Rotation2d.h b/wpimath/src/main/native/include/frc/geometry/Rotation2d.h index 8f89556dd8d..6e9cd8e2a88 100644 --- a/wpimath/src/main/native/include/frc/geometry/Rotation2d.h +++ b/wpimath/src/main/native/include/frc/geometry/Rotation2d.h @@ -28,18 +28,11 @@ class WPILIB_DLLEXPORT Rotation2d { constexpr Rotation2d() = default; /** - * Constructs a Rotation2d with the given radian value. + * Constructs a Rotation2d with the given angle. * - * @param value The value of the angle in radians. + * @param value The value of the angle. */ - constexpr Rotation2d(units::radian_t value); // NOLINT - - /** - * Constructs a Rotation2d with the given degree value. - * - * @param value The value of the angle in degrees. - */ - constexpr Rotation2d(units::degree_t value); // NOLINT + constexpr Rotation2d(units::angle_unit auto value); // NOLINT /** * Constructs a Rotation2d with the given x and y (cosine and sine) diff --git a/wpimath/src/main/native/include/frc/geometry/Rotation2d.inc b/wpimath/src/main/native/include/frc/geometry/Rotation2d.inc index 0d9e97d4977..a222b362317 100644 --- a/wpimath/src/main/native/include/frc/geometry/Rotation2d.inc +++ b/wpimath/src/main/native/include/frc/geometry/Rotation2d.inc @@ -11,13 +11,10 @@ namespace frc { -constexpr Rotation2d::Rotation2d(units::radian_t value) +constexpr Rotation2d::Rotation2d(units::angle_unit auto value) : m_value(value), - m_cos(gcem::cos(value.to())), - m_sin(gcem::sin(value.to())) {} - -constexpr Rotation2d::Rotation2d(units::degree_t value) - : Rotation2d(units::radian_t{value}) {} + m_cos(gcem::cos(value.template convert().value())), + m_sin(gcem::sin(value.template convert().value())) {} constexpr Rotation2d::Rotation2d(double x, double y) { double magnitude = gcem::hypot(x, y); diff --git a/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveKinematics.h b/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveKinematics.h index 3ea6ee92d3a..0f0cff9faa6 100644 --- a/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveKinematics.h +++ b/wpimath/src/main/native/include/frc/kinematics/DifferentialDriveKinematics.h @@ -88,6 +88,7 @@ class WPILIB_DLLEXPORT DifferentialDriveKinematics return ToTwist2d(end.left - start.left, end.right - start.right); } + /// Differential drive trackwidth. units::meter_t trackWidth; }; } // namespace frc diff --git a/wpimath/src/main/native/include/frc/kinematics/MecanumDriveKinematics.h b/wpimath/src/main/native/include/frc/kinematics/MecanumDriveKinematics.h index 24f450abf7e..f5a53ca9531 100644 --- a/wpimath/src/main/native/include/frc/kinematics/MecanumDriveKinematics.h +++ b/wpimath/src/main/native/include/frc/kinematics/MecanumDriveKinematics.h @@ -138,10 +138,33 @@ class WPILIB_DLLEXPORT MecanumDriveKinematics */ Twist2d ToTwist2d(const MecanumDriveWheelPositions& wheelDeltas) const; - const Translation2d GetFrontLeftWheel() const { return m_frontLeftWheel; } - const Translation2d GetFrontRightWheel() const { return m_frontRightWheel; } - const Translation2d GetRearLeftWheel() const { return m_rearLeftWheel; } - const Translation2d GetRearRightWheel() const { return m_rearRightWheel; } + /** + * Returns the front-left wheel translation. + * + * @return The front-left wheel translation. + */ + const Translation2d& GetFrontLeft() const { return m_frontLeftWheel; } + + /** + * Returns the front-right wheel translation. + * + * @return The front-right wheel translation. + */ + const Translation2d& GetFrontRight() const { return m_frontRightWheel; } + + /** + * Returns the rear-left wheel translation. + * + * @return The rear-left wheel translation. + */ + const Translation2d& GetRearLeft() const { return m_rearLeftWheel; } + + /** + * Returns the rear-right wheel translation. + * + * @return The rear-right wheel translation. + */ + const Translation2d& GetRearRight() const { return m_rearRightWheel; } private: mutable Matrixd<4, 3> m_inverseKinematics; diff --git a/wpimath/src/main/native/include/frc/kinematics/Odometry.h b/wpimath/src/main/native/include/frc/kinematics/Odometry.h index 55c074c02fa..9e242e2586f 100644 --- a/wpimath/src/main/native/include/frc/kinematics/Odometry.h +++ b/wpimath/src/main/native/include/frc/kinematics/Odometry.h @@ -20,6 +20,9 @@ namespace frc { * Teams can use odometry during the autonomous period for complex tasks like * path following. Furthermore, odometry can be used for latency compensation * when using computer-vision systems. + * + * @tparam WheelSpeeds Wheel speeds type. + * @tparam WheelPositions Wheel positions type. */ template class WPILIB_DLLEXPORT Odometry { diff --git a/wpimath/src/main/native/include/frc/spline/Spline.h b/wpimath/src/main/native/include/frc/spline/Spline.h index c195a6755fd..257b167f38c 100644 --- a/wpimath/src/main/native/include/frc/spline/Spline.h +++ b/wpimath/src/main/native/include/frc/spline/Spline.h @@ -44,7 +44,10 @@ class Spline { * dimension. */ struct ControlVector { + /// The x components of the control vector. wpi::array x; + + /// The y components of the control vector. wpi::array y; }; diff --git a/wpimath/src/main/native/include/frc/system/plant/DCMotor.h b/wpimath/src/main/native/include/frc/system/plant/DCMotor.h index de9c3b1b547..a2679452472 100644 --- a/wpimath/src/main/native/include/frc/system/plant/DCMotor.h +++ b/wpimath/src/main/native/include/frc/system/plant/DCMotor.h @@ -26,19 +26,28 @@ class WPILIB_DLLEXPORT DCMotor { units::unit_t>>; + /// Voltage at which the motor constants were measured. units::volt_t nominalVoltage; + + /// Torque when stalled. units::newton_meter_t stallTorque; + + /// Current draw when stalled. units::ampere_t stallCurrent; + + /// Current draw under no load. units::ampere_t freeCurrent; + + /// Angular velocity under no load. units::radians_per_second_t freeSpeed; - // Resistance of motor + /// Motor internal resistance. units::ohm_t R; - // Motor velocity constant + /// Motor velocity constant. radians_per_second_per_volt_t Kv; - // Torque constant + /// Motor torque constant. newton_meters_per_ampere_t Kt; /** diff --git a/wpimath/src/main/native/include/frc/system/plant/LinearSystemId.h b/wpimath/src/main/native/include/frc/system/plant/LinearSystemId.h index 76979d7b620..ad5d3b0e28c 100644 --- a/wpimath/src/main/native/include/frc/system/plant/LinearSystemId.h +++ b/wpimath/src/main/native/include/frc/system/plant/LinearSystemId.h @@ -20,6 +20,9 @@ #include "units/voltage.h" namespace frc { +/** + * Linear system ID utility functions. + */ class WPILIB_DLLEXPORT LinearSystemId { public: template diff --git a/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.h b/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.h index f45987b7d0c..9f836c10363 100644 --- a/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.h +++ b/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.h @@ -55,38 +55,82 @@ class ExponentialProfile { using KA = units::compound_unit>; using kA_t = units::unit_t; + /** + * Profile timing. + */ + class ProfileTiming { + public: + /// Profile inflection time. + units::second_t inflectionTime; + + /// Total profile time. + units::second_t totalTime; + + /** + * Decides if the profile is finished by time t. + * + * @param t The time since the beginning of the profile. + * @return if the profile is finished at time t. + */ + bool IsFinished(const units::second_t& t) const { return t >= totalTime; } + }; + + /** + * Profile constraints. + */ class Constraints { public: + /** + * Constructs constraints for an ExponentialProfile. + * + * @param maxInput maximum unsigned input voltage + * @param A The State-Space 1x1 system matrix. + * @param B The State-Space 1x1 input matrix. + */ Constraints(Input_t maxInput, A_t A, B_t B) : maxInput{maxInput}, A{A}, B{B} {} + + /** + * Constructs constraints for an ExponentialProfile from characteristics. + * + * @param maxInput maximum unsigned input voltage + * @param kV The velocity gain. + * @param kA The acceleration gain. + */ Constraints(Input_t maxInput, kV_t kV, kA_t kA) : maxInput{maxInput}, A{-kV / kA}, B{1 / kA} {} + + /** + * Computes the max achievable velocity for an Exponential Profile. + * + * @return The seady-state velocity achieved by this profile. + */ Velocity_t MaxVelocity() const { return -maxInput * B / A; } + /// Maximum unsigned input voltage. Input_t maxInput{0}; + + /// The State-Space 1x1 system matrix. A_t A{0}; + + /// The State-Space 1x1 input matrix. B_t B{0}; }; + /** Profile state. */ class State { public: + /// The position at this state. Distance_t position{0}; - Velocity_t velocity{0}; - bool operator==(const State&) const = default; - }; - class ProfileTiming { - public: - units::second_t inflectionTime; - units::second_t totalTime; + /// The velocity at this state. + Velocity_t velocity{0}; - bool IsFinished(const units::second_t& time) const { - return time > totalTime; - } + bool operator==(const State&) const = default; }; /** - * Construct a ExponentialProfile. + * Constructs a ExponentialProfile. * * @param constraints The constraints on the profile, like maximum input. */ @@ -98,41 +142,72 @@ class ExponentialProfile { ExponentialProfile& operator=(ExponentialProfile&&) = default; /** - * Calculate the correct position and velocity for the profile at a time t - * where the current state is at time t = 0. + * Calculates the position and velocity for the profile at a time t where the + * current state is at time t = 0. + * + * @param t How long to advance from the current state toward the desired + * state. + * @param current The current state. + * @param goal The desired state when the profile is complete. + * @return The position and velocity of the profile at time t. */ State Calculate(const units::second_t& t, const State& current, const State& goal) const; /** - * Calculate the point after which the fastest way to reach the goal state is + * Calculates the point after which the fastest way to reach the goal state is * to apply input in the opposite direction. + * + * @param current The current state. + * @param goal The desired state when the profile is complete. + * @return The position and velocity of the profile at the inflection point. */ State CalculateInflectionPoint(const State& current, const State& goal) const; /** - * Calculate the time it will take for this profile to reach the goal state. + * Calculates the time it will take for this profile to reach the goal state. + * + * @param current The current state. + * @param goal The desired state when the profile is complete. + * @return The total duration of this profile. */ units::second_t TimeLeftUntil(const State& current, const State& goal) const; /** - * Calculate the time it will take for this profile to reach the inflection + * Calculates the time it will take for this profile to reach the inflection * point, and the time it will take for this profile to reach the goal state. + * + * @param current The current state. + * @param goal The desired state when the profile is complete. + * @return The timing information for this profile. */ ProfileTiming CalculateProfileTiming(const State& current, const State& goal) const; private: /** - * Calculate the point after which the fastest way to reach the goal state is + * Calculates the point after which the fastest way to reach the goal state is * to apply input in the opposite direction. + * + * @param current The current state. + * @param goal The desired state when the profile is complete. + * @param input The signed input applied to this profile from the current + * state. + * @return The position and velocity of the profile at the inflection point. */ State CalculateInflectionPoint(const State& current, const State& goal, const Input_t& input) const; /** - * Calculate the time it will take for this profile to reach the inflection + * Calculates the time it will take for this profile to reach the inflection * point, and the time it will take for this profile to reach the goal state. + * + * @param current The current state. + * @param inflectionPoint The inflection point of this profile. + * @param goal The desired state when the profile is complete. + * @param input The signed input applied to this profile from the current + * state. + * @return The timing information for this profile. */ ProfileTiming CalculateProfileTiming(const State& current, const State& inflectionPoint, @@ -140,40 +215,70 @@ class ExponentialProfile { const Input_t& input) const; /** - * Calculate the velocity reached after t seconds when applying an input from + * Calculates the position reached after t seconds when applying an input from * the initial state. + * + * @param t The time since the initial state. + * @param input The signed input applied to this profile from the initial + * state. + * @param initial The initial state. + * @return The distance travelled by this profile. */ - Velocity_t ComputeVelocityFromTime(const units::second_t& time, + Distance_t ComputeDistanceFromTime(const units::second_t& time, const Input_t& input, const State& initial) const; /** - * Calculate the position reached after t seconds when applying an input from + * Calculates the velocity reached after t seconds when applying an input from * the initial state. + * + * @param t The time since the initial state. + * @param input The signed input applied to this profile from the initial + * state. + * @param initial The initial state. + * @return The distance travelled by this profile. */ - Distance_t ComputeDistanceFromTime(const units::second_t& time, + Velocity_t ComputeVelocityFromTime(const units::second_t& time, const Input_t& input, const State& initial) const; /** - * Calculate the distance reached at the same time as the given velocity when + * Calculates the time required to reach a specified velocity given the + * initial velocity. + * + * @param velocity The goal velocity. + * @param input The signed input applied to this profile from the initial + * state. + * @param initial The initial velocity. + * @return The time required to reach the goal velocity. + */ + units::second_t ComputeTimeFromVelocity(const Velocity_t& velocity, + const Input_t& input, + const Velocity_t& initial) const; + + /** + * Calculates the distance reached at the same time as the given velocity when * applying the given input from the initial state. + * + * @param velocity The velocity reached by this profile + * @param input The signed input applied to this profile from the initial + * state. + * @param initial The initial state. + * @return The distance reached when the given velocity is reached. */ Distance_t ComputeDistanceFromVelocity(const Velocity_t& velocity, const Input_t& input, const State& initial) const; /** - * Calculate the time required to reach a specified velocity given the initial - * velocity. - */ - units::second_t ComputeTimeFromVelocity(const Velocity_t& velocity, - const Input_t& input, - const Velocity_t& initial) const; - - /** - * Calculate the velocity at which input should be reversed in order to reach + * Calculates the velocity at which input should be reversed in order to reach * the goal state from the current state. + * + * @param input The signed input applied to this profile from the current + * state. + * @param current The current state. + * @param goal The goal state. + * @return The inflection velocity. */ Velocity_t SolveForInflectionVelocity(const Input_t& input, const State& current, @@ -182,8 +287,11 @@ class ExponentialProfile { /** * Returns true if the profile should be inverted. * - *

The profile is inverted if we should first apply negative input in order - * to reach the goal state. + * The profile is inverted if we should first apply negative input in order to + * reach the goal state. + * + * @param current The initial state (usually the current state). + * @param goal The desired state when the profile is complete. */ bool ShouldFlipInput(const State& current, const State& goal) const; diff --git a/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.inc b/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.inc index ded52452e53..f8fd5f0ac5a 100644 --- a/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.inc +++ b/wpimath/src/main/native/include/frc/trajectory/ExponentialProfile.inc @@ -50,23 +50,6 @@ ExponentialProfile::CalculateInflectionPoint( return CalculateInflectionPoint(current, goal, u); } -template -typename ExponentialProfile::State -ExponentialProfile::CalculateInflectionPoint( - const State& current, const State& goal, const Input_t& input) const { - auto u = input; - - if (current == goal) { - return current; - } - - auto inflectionVelocity = SolveForInflectionVelocity(u, current, goal); - auto inflectionPosition = - ComputeDistanceFromVelocity(inflectionVelocity, -u, goal); - - return {inflectionPosition, inflectionVelocity}; -} - template units::second_t ExponentialProfile::TimeLeftUntil( const State& current, const State& goal) const { @@ -86,6 +69,23 @@ ExponentialProfile::CalculateProfileTiming( return CalculateProfileTiming(current, inflectionPoint, goal, u); } +template +typename ExponentialProfile::State +ExponentialProfile::CalculateInflectionPoint( + const State& current, const State& goal, const Input_t& input) const { + auto u = input; + + if (current == goal) { + return current; + } + + auto inflectionVelocity = SolveForInflectionVelocity(u, current, goal); + auto inflectionPosition = + ComputeDistanceFromVelocity(inflectionVelocity, -u, goal); + + return {inflectionPosition, inflectionVelocity}; +} + template typename ExponentialProfile::ProfileTiming ExponentialProfile::CalculateProfileTiming( diff --git a/wpimath/src/main/native/include/frc/trajectory/Trajectory.h b/wpimath/src/main/native/include/frc/trajectory/Trajectory.h index 2ad972ea490..753bf9e2d14 100644 --- a/wpimath/src/main/native/include/frc/trajectory/Trajectory.h +++ b/wpimath/src/main/native/include/frc/trajectory/Trajectory.h @@ -28,19 +28,19 @@ class WPILIB_DLLEXPORT Trajectory { * Represents one point on the trajectory. */ struct WPILIB_DLLEXPORT State { - // The time elapsed since the beginning of the trajectory. + /// The time elapsed since the beginning of the trajectory. units::second_t t = 0_s; - // The speed at that point of the trajectory. + /// The speed at that point of the trajectory. units::meters_per_second_t velocity = 0_mps; - // The acceleration at that point of the trajectory. + /// The acceleration at that point of the trajectory. units::meters_per_second_squared_t acceleration = 0_mps_sq; - // The pose at that point of the trajectory. + /// The pose at that point of the trajectory. Pose2d pose; - // The curvature at that point of the trajectory. + /// The curvature at that point of the trajectory. units::curvature_t curvature{0.0}; /** diff --git a/wpimath/src/main/native/include/frc/trajectory/TrajectoryUtil.h b/wpimath/src/main/native/include/frc/trajectory/TrajectoryUtil.h index 3ba882f1ac6..48a0a49a9bf 100644 --- a/wpimath/src/main/native/include/frc/trajectory/TrajectoryUtil.h +++ b/wpimath/src/main/native/include/frc/trajectory/TrajectoryUtil.h @@ -12,6 +12,9 @@ #include "frc/trajectory/Trajectory.h" namespace frc { +/** + * Trajectory utilities. + */ class WPILIB_DLLEXPORT TrajectoryUtil { public: TrajectoryUtil() = delete; diff --git a/wpimath/src/main/native/include/frc/trajectory/TrapezoidProfile.h b/wpimath/src/main/native/include/frc/trajectory/TrapezoidProfile.h index 18002462a03..79d22adfeb2 100644 --- a/wpimath/src/main/native/include/frc/trajectory/TrapezoidProfile.h +++ b/wpimath/src/main/native/include/frc/trajectory/TrapezoidProfile.h @@ -52,37 +52,61 @@ class TrapezoidProfile { units::compound_unit>; using Acceleration_t = units::unit_t; + /** + * Profile constraints. + */ class Constraints { public: + /// Maximum velocity. + Velocity_t maxVelocity{0}; + + /// Maximum acceleration. + Acceleration_t maxAcceleration{0}; + + /** + * Default constructor. + */ Constraints() { wpi::math::MathSharedStore::ReportUsage( wpi::math::MathUsageId::kTrajectory_TrapezoidProfile, 1); } - Constraints(Velocity_t maxVelocity_, Acceleration_t maxAcceleration_) - : maxVelocity{maxVelocity_}, maxAcceleration{maxAcceleration_} { + + /** + * Constructs constraints for a Trapezoid Profile. + * + * @param maxVelocity Maximum velocity. + * @param maxAcceleration Maximum acceleration. + */ + Constraints(Velocity_t maxVelocity, Acceleration_t maxAcceleration) + : maxVelocity{maxVelocity}, maxAcceleration{maxAcceleration} { wpi::math::MathSharedStore::ReportUsage( wpi::math::MathUsageId::kTrajectory_TrapezoidProfile, 1); } - Velocity_t maxVelocity{0}; - Acceleration_t maxAcceleration{0}; }; + /** + * Profile state. + */ class State { public: + /// The position at this state. Distance_t position{0}; + + /// The velocity at this state. Velocity_t velocity{0}; + bool operator==(const State&) const = default; }; /** - * Construct a TrapezoidProfile. + * Constructs a TrapezoidProfile. * * @param constraints The constraints on the profile, like maximum velocity. */ TrapezoidProfile(Constraints constraints); // NOLINT /** - * Construct a TrapezoidProfile. + * Constructs a TrapezoidProfile. * * @param constraints The constraints on the profile, like maximum velocity. * @param goal The desired state when the profile is complete. @@ -103,10 +127,12 @@ class TrapezoidProfile { TrapezoidProfile& operator=(TrapezoidProfile&&) = default; /** - * Calculate the correct position and velocity for the profile at a time t - * where the beginning of the profile was at time t = 0. + * Calculates the position and velocity for the profile at a time t where the + * current state is at time t = 0. * - * @param t The time since the beginning of the profile. + * @param t How long to advance from the current state toward the desired + * state. + * @return The position and velocity of the profile at time t. * @deprecated Pass the desired and current state into calculate instead of * constructing a new TrapezoidProfile with the desired and current state */ @@ -117,12 +143,14 @@ class TrapezoidProfile { State Calculate(units::second_t t) const; /** - * Calculate the correct position and velocity for the profile at a time t - * where the beginning of the profile was at time t = 0. + * Calculates the position and velocity for the profile at a time t where the + * current state is at time t = 0. * - * @param t The time since the beginning of the profile. - * @param current The initial state (usually the current state). - * @param goal The desired state when the profile is complete. + * @param t How long to advance from the current state toward the desired + * state. + * @param current The current state. + * @param goal The desired state when the profile is complete. + * @return The position and velocity of the profile at time t. */ State Calculate(units::second_t t, State current, State goal); @@ -130,21 +158,25 @@ class TrapezoidProfile { * Returns the time left until a target distance in the profile is reached. * * @param target The target distance. + * @return The time left until a target distance in the profile is reached. */ units::second_t TimeLeftUntil(Distance_t target) const; /** * Returns the total time the profile takes to reach the goal. + * + * @return The total time the profile takes to reach the goal. */ units::second_t TotalTime() const { return m_endDeccel; } /** * Returns true if the profile has reached the goal. * - * The profile has reached the goal if the time since the profile started - * has exceeded the profile's total time. + * The profile has reached the goal if the time since the profile started has + * exceeded the profile's total time. * * @param t The time since the beginning of the profile. + * @return True if the profile has reached the goal. */ bool IsFinished(units::second_t t) const { return t >= TotalTime(); } @@ -155,7 +187,7 @@ class TrapezoidProfile { * The profile is inverted if goal position is less than the initial position. * * @param initial The initial state (usually the current state). - * @param goal The desired state when the profile is complete. + * @param goal The desired state when the profile is complete. */ static bool ShouldFlipAcceleration(const State& initial, const State& goal) { return initial.position > goal.position; diff --git a/wpimath/src/main/native/include/units/base.h b/wpimath/src/main/native/include/units/base.h index 34cd59e4198..bbb8a2bc4f0 100644 --- a/wpimath/src/main/native/include/units/base.h +++ b/wpimath/src/main/native/include/units/base.h @@ -53,7 +53,6 @@ #if !defined(_MSC_VER) || _MSC_VER > 1800 # define UNIT_HAS_LITERAL_SUPPORT -# define UNIT_HAS_VARIADIC_TEMPLATE_SUPPORT #endif #ifndef UNIT_LIB_DEFAULT_TYPE @@ -358,25 +357,14 @@ template<> inline constexpr const char* abbreviation(const namespaceName::nameSi /** @endcond */\ } -#if defined(UNIT_HAS_VARIADIC_TEMPLATE_SUPPORT) #define UNIT_ADD_IS_UNIT_CATEGORY_TRAIT(unitCategory)\ namespace traits\ {\ template struct is_ ## unitCategory ## _unit : std::integral_constant>::value...>::value> {};\ template inline constexpr bool is_ ## unitCategory ## _unit_v = is_ ## unitCategory ## _unit::value;\ - } -#else -#define UNIT_ADD_IS_UNIT_CATEGORY_TRAIT(unitCategory)\ - namespace traits\ - {\ - template\ - struct is_ ## unitCategory ## _unit : std::integral_constant::type>::value &&\ - units::traits::detail::is_ ## unitCategory ## _unit_impl::type>::value &&\ - units::traits::detail::is_ ## unitCategory ## _unit_impl::type>::value>{};\ - template\ - inline constexpr bool is_ ## unitCategory ## _unit_v = is_ ## unitCategory ## _unit::value;\ - } -#endif + }\ + template \ + concept unitCategory ## _unit = traits::is_ ## unitCategory ## _unit_v; #define UNIT_ADD_CATEGORY_TRAIT(unitCategory)\ UNIT_ADD_CATEGORY_TRAIT_DETAIL(unitCategory)\ diff --git a/wpimath/src/test/java/edu/wpi/first/math/controller/LinearQuadraticRegulatorTest.java b/wpimath/src/test/java/edu/wpi/first/math/controller/LinearQuadraticRegulatorTest.java index e915388cd3d..48278ced6e3 100644 --- a/wpimath/src/test/java/edu/wpi/first/math/controller/LinearQuadraticRegulatorTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/controller/LinearQuadraticRegulatorTest.java @@ -78,8 +78,8 @@ void testLQROnArm() { * *

This is used to test the QRN overload of LQR. * - * @param States Number of states. - * @param Inputs Number of inputs. + * @param Number of states. + * @param Number of inputs. * @param A State matrix. * @param B Input matrix. * @param Q State cost matrix. @@ -119,10 +119,10 @@ void testMatrixOverloadsWithSingleIntegrator() { // QR overload var K = new LinearQuadraticRegulator<>(A, B, Q, R, 0.005).getK(); - assertEquals(0.99750312499512261, K.get(0, 0), 1e-10); + assertEquals(0.9975031249951226, K.get(0, 0), 1e-10); assertEquals(0.0, K.get(0, 1), 1e-10); assertEquals(0.0, K.get(1, 0), 1e-10); - assertEquals(0.99750312499512261, K.get(1, 1), 1e-10); + assertEquals(0.9975031249951226, K.get(1, 1), 1e-10); // QRN overload var N = MatBuilder.fill(Nat.N2(), Nat.N2(), 1, 0, 0, 1); @@ -146,13 +146,13 @@ void testMatrixOverloadsWithDoubleIntegrator() { // QR overload var K = new LinearQuadraticRegulator<>(A, B, Q, R, 0.005).getK(); assertEquals(1.9960017786537287, K.get(0, 0), 1e-10); - assertEquals(0.51182128351092726, K.get(0, 1), 1e-10); + assertEquals(0.5118212835109273, K.get(0, 1), 1e-10); // QRN overload var Aref = MatBuilder.fill(Nat.N2(), Nat.N2(), 0, 1, 0, -Kv / (Ka * 5.0)); var Kimf = getImplicitModelFollowingK(A, B, Q, R, Aref, 0.005); assertEquals(0.0, Kimf.get(0, 0), 1e-10); - assertEquals(-6.9190500116751458e-05, Kimf.get(0, 1), 1e-10); + assertEquals(-6.919050011675146e-05, Kimf.get(0, 1), 1e-10); } @Test diff --git a/wpimath/src/test/java/edu/wpi/first/math/controller/SimpleMotorFeedforwardTest.java b/wpimath/src/test/java/edu/wpi/first/math/controller/SimpleMotorFeedforwardTest.java index e690dafb2c9..6277c030cf7 100644 --- a/wpimath/src/test/java/edu/wpi/first/math/controller/SimpleMotorFeedforwardTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/controller/SimpleMotorFeedforwardTest.java @@ -29,7 +29,7 @@ void testCalculate() { var r = VecBuilder.fill(2.0); var nextR = VecBuilder.fill(3.0); - assertEquals(37.524995834325161 + 0.5, simpleMotor.calculate(2.0, 3.0, dt), 0.002); + assertEquals(37.52499583432516 + 0.5, simpleMotor.calculate(2.0, 3.0, dt), 0.002); assertEquals( plantInversion.calculate(r, nextR).get(0, 0) + Ks, simpleMotor.calculate(2.0, 3.0, dt), diff --git a/wpimath/src/test/java/edu/wpi/first/math/geometry/QuaternionTest.java b/wpimath/src/test/java/edu/wpi/first/math/geometry/QuaternionTest.java index 458b14dbd33..7e6159f573b 100644 --- a/wpimath/src/test/java/edu/wpi/first/math/geometry/QuaternionTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/geometry/QuaternionTest.java @@ -125,7 +125,7 @@ void testTimes() { // Identity var q = new Quaternion( - 0.72760687510899891, 0.29104275004359953, 0.38805700005813276, 0.48507125007266594); + 0.7276068751089989, 0.29104275004359953, 0.38805700005813276, 0.48507125007266594); final var actual2 = q.times(q.inverse()); assertAll( () -> assertEquals(1.0, actual2.getW()), diff --git a/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation2dTest.java b/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation2dTest.java index 7f3e469fada..8a5dee60ea2 100644 --- a/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation2dTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation2dTest.java @@ -112,6 +112,6 @@ void testInterpolate() { rot1 = Rotation2d.fromDegrees(170); rot2 = Rotation2d.fromDegrees(-160); interpolated = rot1.interpolate(rot2, 0.5); - assertEquals(-175.0, interpolated.getDegrees()); + assertEquals(-175.0, interpolated.getDegrees(), kEpsilon); } } diff --git a/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation3dTest.java b/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation3dTest.java index 008a65fabff..896afbfa4f2 100644 --- a/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation3dTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation3dTest.java @@ -362,9 +362,9 @@ void testInterpolate() { rot1 = new Rotation3d(xAxis, Units.degreesToRadians(170)); rot2 = new Rotation3d(xAxis, Units.degreesToRadians(-160)); interpolated = rot1.interpolate(rot2, 0.5); - assertEquals(Units.degreesToRadians(-175.0), interpolated.getX()); + assertEquals(Units.degreesToRadians(-175.0), interpolated.getX(), kEpsilon); assertEquals(Units.degreesToRadians(0.0), interpolated.getY(), kEpsilon); - assertEquals(Units.degreesToRadians(0.0), interpolated.getZ()); + assertEquals(Units.degreesToRadians(0.0), interpolated.getZ(), kEpsilon); // 50 + (70 - 50) * 0.5 = 60 rot1 = new Rotation3d(yAxis, Units.degreesToRadians(50)); diff --git a/wpimath/src/test/native/cpp/kinematics/proto/MecanumDriveKinematicsProtoTest.cpp b/wpimath/src/test/native/cpp/kinematics/proto/MecanumDriveKinematicsProtoTest.cpp index 7051ca1a0c4..c601fc3b4d4 100644 --- a/wpimath/src/test/native/cpp/kinematics/proto/MecanumDriveKinematicsProtoTest.cpp +++ b/wpimath/src/test/native/cpp/kinematics/proto/MecanumDriveKinematicsProtoTest.cpp @@ -24,11 +24,8 @@ TEST(MecanumDriveKinematicsProtoTest, Roundtrip) { ProtoType::Pack(proto, kExpectedData); MecanumDriveKinematics unpacked_data = ProtoType::Unpack(*proto); - EXPECT_EQ(kExpectedData.GetFrontLeftWheel(), - unpacked_data.GetFrontLeftWheel()); - EXPECT_EQ(kExpectedData.GetFrontRightWheel(), - unpacked_data.GetFrontRightWheel()); - EXPECT_EQ(kExpectedData.GetRearLeftWheel(), unpacked_data.GetRearLeftWheel()); - EXPECT_EQ(kExpectedData.GetRearRightWheel(), - unpacked_data.GetRearRightWheel()); + EXPECT_EQ(kExpectedData.GetFrontLeft(), unpacked_data.GetFrontLeft()); + EXPECT_EQ(kExpectedData.GetFrontRight(), unpacked_data.GetFrontRight()); + EXPECT_EQ(kExpectedData.GetRearLeft(), unpacked_data.GetRearLeft()); + EXPECT_EQ(kExpectedData.GetRearRight(), unpacked_data.GetRearRight()); } diff --git a/wpimath/src/test/native/cpp/kinematics/struct/MecanumDriveKinematicsStructTest.cpp b/wpimath/src/test/native/cpp/kinematics/struct/MecanumDriveKinematicsStructTest.cpp index 0b3676d63fd..920c668ae10 100644 --- a/wpimath/src/test/native/cpp/kinematics/struct/MecanumDriveKinematicsStructTest.cpp +++ b/wpimath/src/test/native/cpp/kinematics/struct/MecanumDriveKinematicsStructTest.cpp @@ -23,11 +23,8 @@ TEST(MecanumDriveKinematicsStructTest, Roundtrip) { MecanumDriveKinematics unpacked_data = StructType::Unpack(buffer); - EXPECT_EQ(kExpectedData.GetFrontLeftWheel(), - unpacked_data.GetFrontLeftWheel()); - EXPECT_EQ(kExpectedData.GetFrontRightWheel(), - unpacked_data.GetFrontRightWheel()); - EXPECT_EQ(kExpectedData.GetRearLeftWheel(), unpacked_data.GetRearLeftWheel()); - EXPECT_EQ(kExpectedData.GetRearRightWheel(), - unpacked_data.GetRearRightWheel()); + EXPECT_EQ(kExpectedData.GetFrontLeft(), unpacked_data.GetFrontLeft()); + EXPECT_EQ(kExpectedData.GetFrontRight(), unpacked_data.GetFrontRight()); + EXPECT_EQ(kExpectedData.GetRearLeft(), unpacked_data.GetRearLeft()); + EXPECT_EQ(kExpectedData.GetRearRight(), unpacked_data.GetRearRight()); } diff --git a/wpimath/src/test/native/cpp/trajectory/TrajectoryGeneratorTest.cpp b/wpimath/src/test/native/cpp/trajectory/TrajectoryGeneratorTest.cpp index f2ab4cd59df..ad7173ae9b7 100644 --- a/wpimath/src/test/native/cpp/trajectory/TrajectoryGeneratorTest.cpp +++ b/wpimath/src/test/native/cpp/trajectory/TrajectoryGeneratorTest.cpp @@ -52,6 +52,6 @@ TEST(TrajectoryGenerationTest, CurvatureOptimization) { TrajectoryConfig{12_fps, 12_fps_sq}); for (size_t i = 1; i < t.States().size() - 1; ++i) { - EXPECT_NE(0, t.States()[i].curvature.to()); + EXPECT_NE(0, t.States()[i].curvature.value()); } } diff --git a/wpimath/wpimath-config.cmake.in b/wpimath/wpimath-config.cmake.in index 3618a1fe8c7..8e2c370889b 100644 --- a/wpimath/wpimath-config.cmake.in +++ b/wpimath/wpimath-config.cmake.in @@ -10,5 +10,6 @@ endif() include(${SELF_DIR}/wpimath.cmake) if(@WITH_JAVA@) @WPIUNITS_DEP_REPLACE@ + @FILENAME_DEP_REPLACE@ include(${SELF_DIR}/wpimath_jar.cmake) endif() diff --git a/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceAnnouncer.java b/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceAnnouncer.java index 39989e156d6..27c55b8c957 100644 --- a/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceAnnouncer.java +++ b/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceAnnouncer.java @@ -54,14 +54,21 @@ public void close() { m_cleanable.clean(); } + /** Starts multicast service announcer. */ public void start() { WPINetJNI.startMulticastServiceAnnouncer(m_handle); } + /** Stops multicast service announcer. */ public void stop() { WPINetJNI.stopMulticastServiceAnnouncer(m_handle); } + /** + * Returns true if there's a multicast service announcer implementation. + * + * @return True if there's a multicast service announcer implementation. + */ public boolean hasImplementation() { return WPINetJNI.getMulticastServiceAnnouncerHasImplementation(m_handle); } diff --git a/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceResolver.java b/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceResolver.java index 8d70fd7ee34..fd7fd491119 100644 --- a/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceResolver.java +++ b/wpinet/src/main/java/edu/wpi/first/net/MulticastServiceResolver.java @@ -32,22 +32,39 @@ public void close() { m_cleanable.clean(); } + /** Starts multicast service resolver. */ public void start() { WPINetJNI.startMulticastServiceResolver(m_handle); } + /** Stops multicast service resolver. */ public void stop() { WPINetJNI.stopMulticastServiceResolver(m_handle); } + /** + * Returns true if there's a multicast service resolver implementation. + * + * @return True if there's a multicast service resolver implementation. + */ public boolean hasImplementation() { return WPINetJNI.getMulticastServiceResolverHasImplementation(m_handle); } + /** + * Returns event handle. + * + * @return Event handle. + */ public int getEventHandle() { return WPINetJNI.getMulticastServiceResolverEventHandle(m_handle); } + /** + * Returns multicast service resolver data. + * + * @return Multicast service resolver data. + */ public ServiceData[] getData() { return WPINetJNI.getMulticastServiceResolverData(m_handle); } diff --git a/wpinet/src/main/java/edu/wpi/first/net/ServiceData.java b/wpinet/src/main/java/edu/wpi/first/net/ServiceData.java index 126526b2c75..9dd40ce9d52 100644 --- a/wpinet/src/main/java/edu/wpi/first/net/ServiceData.java +++ b/wpinet/src/main/java/edu/wpi/first/net/ServiceData.java @@ -38,22 +38,47 @@ public ServiceData( } } + /** + * Returns service data payload. + * + * @return Service data payload. + */ public Map getTxt() { return m_txt; } + /** + * Returns host name. + * + * @return Host name. + */ public String getHostName() { return m_hostName; } + /** + * Returns service name. + * + * @return Service name. + */ public String getServiceName() { return m_serviceName; } + /** + * Returns port number. + * + * @return Port number. + */ public int getPort() { return m_port; } + /** + * Returns IPv4 address. + * + * @return IPv4 address. + */ public long getIpv4Address() { return m_ipv4Address; } diff --git a/wpinet/src/main/java/edu/wpi/first/net/WPINetJNI.java b/wpinet/src/main/java/edu/wpi/first/net/WPINetJNI.java index d0bcf7d4a9a..82d8e256491 100644 --- a/wpinet/src/main/java/edu/wpi/first/net/WPINetJNI.java +++ b/wpinet/src/main/java/edu/wpi/first/net/WPINetJNI.java @@ -8,20 +8,35 @@ import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; +/** WPINet JNI. */ public class WPINetJNI { static boolean libraryLoaded = false; static RuntimeLoader 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 { @@ -54,32 +69,118 @@ public static synchronized void forceLoad() throws IOException { libraryLoaded = true; } + /** + * Forward a local TCP port to a remote host and port. Note that local ports less than 1024 won't + * work as a normal user. + * + * @param port local port number + * @param remoteHost remote IP address / DNS name + * @param remotePort remote port number + */ public static native void addPortForwarder(int port, String remoteHost, int remotePort); + /** + * Stop TCP forwarding on a port. + * + * @param port local port number + */ public static native void removePortForwarder(int port); + /** + * Creates a MulticastServiceAnnouncer. + * + * @param serviceName service name + * @param serviceType service type + * @param port port + * @param keys keys + * @param values values + * @return MulticastServiceAnnouncer handle. + */ public static native int createMulticastServiceAnnouncer( String serviceName, String serviceType, int port, String[] keys, String[] values); + /** + * Frees a MulticastServiceAnnouncer. + * + * @param handle MulticastServiceAnnouncer handle. + */ public static native void freeMulticastServiceAnnouncer(int handle); + /** + * Starts MulticastServiceAnnouncer. + * + * @param handle MulticastServiceAnnouncer handle. + */ public static native void startMulticastServiceAnnouncer(int handle); + /** + * Stops MulticastServiceAnnouncer. + * + * @param handle MulticastServiceAnnouncer handle. + */ public static native void stopMulticastServiceAnnouncer(int handle); + /** + * Returns true if MulticastServiceAnnouncer has an implementation. + * + * @param handle MulticastServiceAnnouncer handle. + * @return True if MulticastServiceAnnouncer has an implementation. + */ public static native boolean getMulticastServiceAnnouncerHasImplementation(int handle); + /** + * Creates a MulticastServiceResolver. + * + * @param serviceType Service type. + * @return MulticastServiceResolver handle. + */ public static native int createMulticastServiceResolver(String serviceType); + /** + * Frees MulticastServiceResolver. + * + * @param handle MulticastServiceResolver handle. + */ public static native void freeMulticastServiceResolver(int handle); + /** + * Starts MulticastServiceResolver. + * + * @param handle MulticastServiceResolver handle. + */ public static native void startMulticastServiceResolver(int handle); + /** + * Stops MulticastServiceResolver. + * + * @param handle MulticastServiceResolver handle. + */ public static native void stopMulticastServiceResolver(int handle); + /** + * Returns true if MulticastServiceResolver has an implementation. + * + * @param handle MulticastServiceResolver handle. + * @return True if MulticastServiceResolver has an implementation. + */ public static native boolean getMulticastServiceResolverHasImplementation(int handle); + /** + * Returns event handle for MulticastServiceResolver. + * + * @param handle MulticastServiceResolver handle. + * @return Event handle for MulticastServiceResolver. + */ public static native int getMulticastServiceResolverEventHandle(int handle); + /** + * Returns service data for MulticastServiceResolver. + * + * @param handle MulticastServiceResolver handle. + * @return Service data for MulticastServiceResolver. + */ public static native ServiceData[] getMulticastServiceResolverData(int handle); + + /** Utility class. */ + private WPINetJNI() {} } diff --git a/wpinet/src/main/native/cpp/UDPClient.cpp b/wpinet/src/main/native/cpp/UDPClient.cpp index 1596b59ab01..1e9bc872ddf 100644 --- a/wpinet/src/main/native/cpp/UDPClient.cpp +++ b/wpinet/src/main/native/cpp/UDPClient.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #endif diff --git a/wpinet/src/main/native/cpp/WebSocket.cpp b/wpinet/src/main/native/cpp/WebSocket.cpp index 43b901ecb68..25eaa0fc22b 100644 --- a/wpinet/src/main/native/cpp/WebSocket.cpp +++ b/wpinet/src/main/native/cpp/WebSocket.cpp @@ -25,14 +25,21 @@ using namespace wpi; -#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG static std::string DebugBinary(std::span val) { #ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG_CONTENT std::string str; wpi::raw_string_ostream stros{str}; + size_t limited = 0; + if (val.size() > 30) { + limited = val.size(); + val = val.subspan(0, 30); + } for (auto ch : val) { stros << fmt::format("{:02x},", static_cast(ch) & 0xff); } + if (limited != 0) { + stros << fmt::format("... (total {})", limited); + } return str; #else return ""; @@ -46,7 +53,6 @@ static inline std::string_view DebugText(std::string_view val) { return ""; #endif } -#endif // WPINET_WEBSOCKET_VERBOSE_DEBUG class WebSocket::WriteReq : public uv::WriteReq, public detail::WebSocketWriteReqBase { @@ -61,7 +67,7 @@ class WebSocket::WriteReq : public uv::WriteReq, void Send(uv::Error err) { auto ws = m_ws.lock(); if (!ws || err) { - WS_DEBUG("no WS or error, calling callback\n"); + // WS_DEBUG("no WS or error, calling callback\n"); m_frames.ReleaseBufs(); m_callback(m_userBufs, err); return; @@ -71,18 +77,18 @@ class WebSocket::WriteReq : public uv::WriteReq, if (m_controlCont) { // We have a control frame; switch to it. We will come back here via // the control frame's m_cont when it's done. - WS_DEBUG("Continuing with a control write\n"); + WS_DEBUG(ws->GetStream(), "Continuing with a control write"); auto controlCont = std::move(m_controlCont); m_controlCont.reset(); return controlCont->Send({}); } int result = Continue(ws->m_stream, shared_from_this()); - WS_DEBUG("Continue() -> {}\n", result); + WS_DEBUG(ws->GetStream(), "Continue() -> {}", result); if (result <= 0) { m_frames.ReleaseBufs(); m_callback(m_userBufs, uv::Error{result}); if (result == 0 && m_cont) { - WS_DEBUG("Continuing with another write\n"); + WS_DEBUG(ws->GetStream(), "Continuing with another write"); ws->m_curWriteReq = m_cont; return m_cont->Send({}); } else { @@ -418,6 +424,8 @@ static inline void Unmask(std::span data, } void WebSocket::HandleIncoming(uv::Buffer& buf, size_t size) { + m_lastReceivedTime = m_stream.GetLoopRef().Now().count(); + // ignore incoming data if we're failed or closed if (m_state == FAILED || m_state == CLOSED) { return; @@ -555,7 +563,7 @@ void WebSocket::HandleIncoming(uv::Buffer& buf, size_t size) { uint8_t opcode = m_header[0] & kOpMask; switch (opcode) { case kOpCont: - WS_DEBUG("WS Fragment {} [{}]\n", m_payload.size(), + WS_DEBUG(m_stream, "WS Fragment {} [{}]", m_payload.size(), DebugBinary(m_payload)); switch (m_fragmentOpcode) { case kOpText: @@ -563,15 +571,15 @@ void WebSocket::HandleIncoming(uv::Buffer& buf, size_t size) { std::string_view content{ reinterpret_cast(m_payload.data()), m_payload.size()}; - WS_DEBUG("WS RecvText(Defrag) {} ({})\n", m_payload.size(), - DebugText(content)); + WS_DEBUG(m_stream, "WS RecvText(Defrag) {} ({})", + m_payload.size(), DebugText(content)); text(content, fin); } break; case kOpBinary: if (!m_combineFragments || fin) { - WS_DEBUG("WS RecvBinary(Defrag) {} ({})\n", m_payload.size(), - DebugBinary(m_payload)); + WS_DEBUG(m_stream, "WS RecvBinary(Defrag) {} ({})", + m_payload.size(), DebugBinary(m_payload)); binary(m_payload, fin); } break; @@ -587,34 +595,35 @@ void WebSocket::HandleIncoming(uv::Buffer& buf, size_t size) { std::string_view content{reinterpret_cast(m_payload.data()), m_payload.size()}; if (m_fragmentOpcode != 0) { - WS_DEBUG("WS RecvText {} ({}) -> INCOMPLETE FRAGMENT\n", + WS_DEBUG(m_stream, "WS RecvText {} ({}) -> INCOMPLETE FRAGMENT", m_payload.size(), DebugText(content)); return Fail(1002, "incomplete fragment"); } if (!m_combineFragments || fin) { - WS_DEBUG("WS RecvText {} ({})\n", m_payload.size(), + WS_DEBUG(m_stream, "WS RecvText {} ({})", m_payload.size(), DebugText(content)); text(content, fin); } if (!fin) { - WS_DEBUG("WS RecvText {} StartFrag\n", m_payload.size()); + WS_DEBUG(m_stream, "WS RecvText {} StartFrag", m_payload.size()); m_fragmentOpcode = opcode; } break; } case kOpBinary: if (m_fragmentOpcode != 0) { - WS_DEBUG("WS RecvBinary {} ({}) -> INCOMPLETE FRAGMENT\n", + WS_DEBUG(m_stream, "WS RecvBinary {} ({}) -> INCOMPLETE FRAGMENT", m_payload.size(), DebugBinary(m_payload)); return Fail(1002, "incomplete fragment"); } if (!m_combineFragments || fin) { - WS_DEBUG("WS RecvBinary {} ({})\n", m_payload.size(), + WS_DEBUG(m_stream, "WS RecvBinary {} ({})", m_payload.size(), DebugBinary(m_payload)); binary(m_payload, fin); } if (!fin) { - WS_DEBUG("WS RecvBinary {} StartFrag\n", m_payload.size()); + WS_DEBUG(m_stream, "WS RecvBinary {} StartFrag", + m_payload.size()); m_fragmentOpcode = opcode; } break; @@ -638,7 +647,7 @@ void WebSocket::HandleIncoming(uv::Buffer& buf, size_t size) { if (m_state != CLOSING) { SendClose(code, reason); } - SetClosed(code, reason); + SetClosed(code, fmt::format("remote close: {}", reason)); // If we're the server, shutdown the connection. if (m_server) { Shutdown(); @@ -662,7 +671,7 @@ void WebSocket::HandleIncoming(uv::Buffer& buf, size_t size) { } }); } - WS_DEBUG("WS RecvPing() {} ({})\n", m_controlPayload.size(), + WS_DEBUG(m_stream, "WS RecvPing() {} ({})", m_controlPayload.size(), DebugBinary(m_controlPayload)); ping(m_controlPayload); break; @@ -670,12 +679,13 @@ void WebSocket::HandleIncoming(uv::Buffer& buf, size_t size) { if (!fin) { return Fail(1002, "cannot fragment control frames"); } - WS_DEBUG("WS RecvPong() {} ({})\n", m_controlPayload.size(), + WS_DEBUG(m_stream, "WS RecvPong() {} ({})", m_controlPayload.size(), DebugBinary(m_controlPayload)); pong(m_controlPayload); break; default: - return Fail(1002, "invalid message opcode"); + return Fail(1002, fmt::format("invalid message opcode {}", + static_cast(opcode))); } // Prepare for next message @@ -735,7 +745,7 @@ void WebSocket::SendFrames( std::span frames, std::function, uv::Error)> callback) { // If we're not open, emit an error and don't send the data - WS_DEBUG("SendFrames({})\n", frames.size()); + WS_DEBUG(m_stream, "SendFrames({})", frames.size()); if (m_state != OPEN) { SendError(frames, callback); return; diff --git a/wpinet/src/main/native/cpp/WebSocketDebug.h b/wpinet/src/main/native/cpp/WebSocketDebug.h index 5653b5f7838..3f588361a31 100644 --- a/wpinet/src/main/native/cpp/WebSocketDebug.h +++ b/wpinet/src/main/native/cpp/WebSocketDebug.h @@ -4,18 +4,15 @@ #pragma once -#include +#include -// #define WPINET_WEBSOCKET_VERBOSE_DEBUG -// #define WPINET_WEBSOCKET_VERBOSE_DEBUG_CONTENT +#define WPINET_WEBSOCKET_VERBOSE_DEBUG_CONTENT #ifdef __clang__ #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" #endif -#ifdef WPINET_WEBSOCKET_VERBOSE_DEBUG -#define WS_DEBUG(format, ...) \ - ::fmt::print(FMT_STRING(format) __VA_OPT__(, ) __VA_ARGS__) -#else -#define WS_DEBUG(fmt, ...) -#endif +#define WS_DEBUG(stream, format, ...) \ + if (auto logger_ = stream.GetLogger()) { \ + WPI_DEBUG4(*logger_, "WS: " format __VA_OPT__(, ) __VA_ARGS__) \ + } diff --git a/wpinet/src/main/native/cpp/WebSocketSerializer.h b/wpinet/src/main/native/cpp/WebSocketSerializer.h index 264b8f594db..3a8e6b46b9d 100644 --- a/wpinet/src/main/native/cpp/WebSocketSerializer.h +++ b/wpinet/src/main/native/cpp/WebSocketSerializer.h @@ -73,7 +73,7 @@ int WebSocketWriteReqBase::Continue(Stream& stream, std::shared_ptr req) { } int sentBytes = stream.TryWrite(bufs); - WS_DEBUG("TryWrite({}) -> {} (expected {})\n", bufs.size(), sentBytes, + WS_DEBUG(stream, "TryWrite({}) -> {} (expected {})", bufs.size(), sentBytes, numBytes); if (sentBytes < 0) { return sentBytes; // error @@ -133,10 +133,10 @@ int WebSocketWriteReqBase::Continue(Stream& stream, std::shared_ptr req) { m_continueBufPos += bufIt - bufs.begin(); if (writeBufs.empty()) { - WS_DEBUG("Write Done\n"); + WS_DEBUG(stream, "Write Done"); return 0; } - WS_DEBUG("Write({})\n", writeBufs.size()); + WS_DEBUG(stream, "Write({})", writeBufs.size()); stream.Write(writeBufs, req); return 1; } @@ -146,7 +146,7 @@ std::span TrySendFrames( bool server, Stream& stream, std::span frames, MakeReq&& makeReq, std::function, uv::Error)> callback) { - WS_DEBUG("TrySendFrames({})\n", frames.size()); + WS_DEBUG(stream, "TrySendFrames({})", frames.size()); auto frameIt = frames.begin(); auto frameEnd = frames.end(); while (frameIt != frameEnd) { @@ -157,8 +157,8 @@ std::span TrySendFrames( SmallVector frameOffs; int numBytes = 0; while (frameIt != frameEnd) { - frameOffs.emplace_back(numBytes); numBytes += sendFrames.AddFrame(*frameIt++, server); + frameOffs.emplace_back(numBytes); if ((server && (numBytes >= 65536 || frameOffs.size() > 32)) || (!server && numBytes >= 8192)) { // don't waste too much memory or effort on header generation or masking @@ -168,8 +168,8 @@ std::span TrySendFrames( // try to send int sentBytes = stream.TryWrite(sendFrames.m_bufs); - WS_DEBUG("TryWrite({}) -> {} (expected {})\n", sendFrames.m_bufs.size(), - sentBytes, numBytes); + WS_DEBUG(stream, "TryWrite({}) -> {} (expected {})", + sendFrames.m_bufs.size(), sentBytes, numBytes); if (sentBytes == 0) { // we haven't started a frame yet; clean up any bufs that have actually @@ -200,15 +200,15 @@ std::span TrySendFrames( // figure out what the last (partially) frame sent actually was auto offIt = frameOffs.begin(); auto offEnd = frameOffs.end(); - bool isFin = true; while (offIt != offEnd && *offIt < sentBytes) { ++offIt; - isFin = (frameStart->opcode & WebSocket::kFlagFin) != 0; ++frameStart; } + bool isFin = (frameStart->opcode & WebSocket::kFlagFin) != 0; if (offIt != offEnd && *offIt == sentBytes && isFin) { // we finished at a normal FIN frame boundary; no need for a Write() + ++frameStart; SmallVector bufs; for (auto it = frames.begin(); it != frameStart; ++it) { bufs.append(it->data.begin(), it->data.end()); @@ -239,12 +239,13 @@ std::span TrySendFrames( } // continue through the last buffer of the last partial frame - while (bufIt != bufEnd && offIt != offEnd && pos < *offIt) { - pos += bufIt->len; - writeBufs.emplace_back(*bufIt++); - } if (offIt != offEnd) { + while (bufIt != bufEnd && pos < *offIt) { + pos += bufIt->len; + writeBufs.emplace_back(*bufIt++); + } ++offIt; + ++frameStart; } // move allocated buffers into request @@ -258,7 +259,7 @@ std::span TrySendFrames( while (frameStart != frameEnd && !isFin) { if (offIt != offEnd) { // we already generated the wire buffers for this frame, use them - while (pos < *offIt && bufIt != bufEnd) { + while (bufIt != bufEnd && pos < *offIt) { pos += bufIt->len; continuePos += bufIt->len; req->m_frames.m_bufs.emplace_back(*bufIt++); @@ -280,7 +281,7 @@ std::span TrySendFrames( req->m_userBufs.append(it->data.begin(), it->data.end()); } - WS_DEBUG("Write({})\n", writeBufs.size()); + WS_DEBUG(stream, "Write({})", writeBufs.size()); stream.Write(writeBufs, req); #ifdef __clang__ // work around clang bug diff --git a/wpinet/src/main/native/cpp/uv/Stream.cpp b/wpinet/src/main/native/cpp/uv/Stream.cpp index e054003405e..164b30ac2e1 100644 --- a/wpinet/src/main/native/cpp/uv/Stream.cpp +++ b/wpinet/src/main/native/cpp/uv/Stream.cpp @@ -4,7 +4,9 @@ #include "wpinet/uv/Stream.h" +#include #include +#include using namespace wpi; using namespace wpi::uv; @@ -86,11 +88,38 @@ void Stream::StartRead() { }); } +static std::string BufsToString(std::span bufs) { + std::string str; + wpi::raw_string_ostream stros{str}; + size_t count = 0; + for (auto buf : bufs) { + for (auto ch : buf.bytes()) { + stros << fmt::format("{:02x},", static_cast(ch) & 0xff); + if (count++ > 30) { + goto extra; + } + } + } + goto done; +extra: { + size_t total = 0; + for (auto buf : bufs) { + total += buf.len; + } + stros << fmt::format("... (total {})", total); +} +done: + return str; +} + void Stream::Write(std::span bufs, const std::shared_ptr& req) { if (IsLoopClosing()) { return; } + if (auto logger = GetLogger()) { + WPI_DEBUG4(*logger, "uv::Write({})", BufsToString(bufs)); + } if (Invoke(&uv_write, req->GetRaw(), GetRawStream(), bufs.data(), bufs.size(), [](uv_write_t* r, int status) { auto& h = *static_cast(r->data); @@ -113,6 +142,9 @@ int Stream::TryWrite(std::span bufs) { if (IsLoopClosing()) { return UV_ECANCELED; } + if (auto logger = GetLogger()) { + WPI_DEBUG4(*logger, "uv::TryWrite({})", BufsToString(bufs)); + } int val = uv_try_write(GetRawStream(), bufs.data(), bufs.size()); if (val == UV_EAGAIN) { return 0; diff --git a/wpinet/src/main/native/include/wpinet/MulticastServiceAnnouncer.h b/wpinet/src/main/native/include/wpinet/MulticastServiceAnnouncer.h index 2892919e036..ee1ede2fa7c 100644 --- a/wpinet/src/main/native/include/wpinet/MulticastServiceAnnouncer.h +++ b/wpinet/src/main/native/include/wpinet/MulticastServiceAnnouncer.h @@ -15,17 +15,51 @@ namespace wpi { class MulticastServiceAnnouncer { public: + /** + * Creates a MulticastServiceAnnouncer. + * + * @param serviceName service name + * @param serviceType service type + * @param port port + * @param txt txt + */ MulticastServiceAnnouncer( std::string_view serviceName, std::string_view serviceType, int port, std::span> txt); + /** + * Creates a MulticastServiceAnnouncer. + * + * @param serviceName service name + * @param serviceType service type + * @param port port + * @param txt txt + */ MulticastServiceAnnouncer( std::string_view serviceName, std::string_view serviceType, int port, std::span> txt); + /** + * Creates a MulticastServiceAnnouncer. + * + * @param serviceName service name + * @param serviceType service type + * @param port port + */ MulticastServiceAnnouncer(std::string_view serviceName, std::string_view serviceType, int port); ~MulticastServiceAnnouncer() noexcept; + /** + * Starts multicast service announcer. + */ void Start(); + /** + * Stops multicast service announcer. + */ void Stop(); + /** + * Returns true if there's a multicast service announcer implementation. + * + * @return True if there's a multicast service announcer implementation. + */ bool HasImplementation() const; struct Impl; diff --git a/wpinet/src/main/native/include/wpinet/MulticastServiceResolver.h b/wpinet/src/main/native/include/wpinet/MulticastServiceResolver.h index f46a8e4bd8c..c07863ef3b2 100644 --- a/wpinet/src/main/native/include/wpinet/MulticastServiceResolver.h +++ b/wpinet/src/main/native/include/wpinet/MulticastServiceResolver.h @@ -21,15 +21,36 @@ class MulticastServiceResolver { explicit MulticastServiceResolver(std::string_view serviceType); ~MulticastServiceResolver() noexcept; struct ServiceData { + /// IPv4 address. unsigned int ipv4Address; + /// Port number. int port; + /// Service name. std::string serviceName; + /// Host name. std::string hostName; + /// Service data payload. std::vector> txt; }; + /** + * Starts multicast service resolver. + */ void Start(); + /** + * Stops multicast service resolver. + */ void Stop(); + /** + * Returns event handle. + * + * @return Event handle. + */ WPI_EventHandle GetEventHandle() const { return event.GetHandle(); } + /** + * Returns multicast service resolver data. + * + * @return Multicast service resolver data. + */ std::vector GetData() { std::scoped_lock lock{mutex}; event.Reset(); @@ -40,6 +61,11 @@ class MulticastServiceResolver { queue.swap(ret); return ret; } + /** + * Returns true if there's a multicast service resolver implementation. + * + * @return True if there's a multicast service resolver implementation. + */ bool HasImplementation() const; struct Impl; @@ -55,6 +81,9 @@ class MulticastServiceResolver { std::unique_ptr pImpl; }; } // namespace wpi +#endif + +#ifdef __cplusplus extern "C" { #endif diff --git a/wpinet/src/main/native/include/wpinet/WebSocket.h b/wpinet/src/main/native/include/wpinet/WebSocket.h index 297c1235fd0..ef7cbdb2c4b 100644 --- a/wpinet/src/main/native/include/wpinet/WebSocket.h +++ b/wpinet/src/main/native/include/wpinet/WebSocket.h @@ -457,6 +457,12 @@ class WebSocket : public std::enable_shared_from_this { */ void Shutdown(); + /** + * Gets the last time data was received on the stream. + * @return Timestamp + */ + uint64_t GetLastReceivedTime() const { return m_lastReceivedTime; } + /** * Open event. Emitted when the connection is open and ready to communicate. * The parameter is the selected subprotocol. @@ -521,6 +527,7 @@ class WebSocket : public std::enable_shared_from_this { State m_state = CONNECTING; // incoming message buffers/state + uint64_t m_lastReceivedTime = 0; SmallVector m_header; size_t m_headerSize = 0; SmallVector m_payload; diff --git a/wpinet/src/main/native/include/wpinet/uv/Handle.h b/wpinet/src/main/native/include/wpinet/uv/Handle.h index 903d43cec49..9da00b5e3aa 100644 --- a/wpinet/src/main/native/include/wpinet/uv/Handle.h +++ b/wpinet/src/main/native/include/wpinet/uv/Handle.h @@ -19,6 +19,10 @@ #include "wpinet/uv/Error.h" #include "wpinet/uv/Loop.h" +namespace wpi { +class Logger; +} // namespace wpi + namespace wpi::uv { /** @@ -219,6 +223,18 @@ class Handle : public std::enable_shared_from_this { */ void SetData(std::shared_ptr data) { m_data = std::move(data); } + /** + * Sets logger. + * @param logger Logger + */ + void SetLogger(Logger* logger) { m_logger = logger; } + + /** + * Gets logger. + * @return Logger, or nullptr if none set + */ + Logger* GetLogger() const { return m_logger; } + /** * Error signal */ @@ -264,6 +280,7 @@ class Handle : public std::enable_shared_from_this { std::function m_allocBuf{&Buffer::Allocate}; std::function m_freeBuf{&DefaultFreeBuf}; std::shared_ptr m_data; + Logger* m_logger = nullptr; }; /** diff --git a/wpinet/src/test/native/cpp/WebSocketSerializerTest.cpp b/wpinet/src/test/native/cpp/WebSocketSerializerTest.cpp index 6767a23c685..f1e2629331a 100644 --- a/wpinet/src/test/native/cpp/WebSocketSerializerTest.cpp +++ b/wpinet/src/test/native/cpp/WebSocketSerializerTest.cpp @@ -67,6 +67,8 @@ class MockStream { MOCK_METHOD(void, DoWrite, (std::span bufs, const std::shared_ptr& req)); + + Logger* GetLogger() const { return nullptr; } }; class WebSocketWriteReqTest : public ::testing::Test { @@ -376,4 +378,42 @@ TEST_F(WebSocketTrySendTest, ServerPartialLastFrame) { ASSERT_EQ(callbackCalled, 0); } +TEST_F(WebSocketTrySendTest, Big) { + std::vector bufs; + for (int i = 0; i < 100000;) { + i += 1430; + bufs.emplace_back( + uv::Buffer::Allocate(i < 100000 ? 1430 : (100000 - (i - 1430)))); + } + WebSocket::Frame frame{WebSocket::kOpBinary | WebSocket::kFlagFin, bufs}; + EXPECT_CALL(stream, TryWrite(_)).WillOnce(Return(7681)); + + // Write called for remainder of buffers + std::vector remBufs; + remBufs.emplace_back(bufs[5]); + remBufs.back().base += 521; + remBufs.back().len -= 521; + for (size_t i = 6; i < bufs.size(); ++i) { + remBufs.emplace_back(bufs[i]); + } + EXPECT_CALL(stream, DoWrite(wpi::SpanEq(remBufs), _)); + + ASSERT_TRUE( + TrySendFrames( + true, stream, {{frame}}, + [&](std::function, uv::Error)>&& cb) { + ++makeReqCalled; + req = std::make_shared(std::move(cb)); + return req; + }, + [&](auto bufs, auto err) { ++callbackCalled; }) + .empty()); + for (auto& buf : bufs) { + buf.Deallocate(); + } + ASSERT_EQ(makeReqCalled, 1); + ASSERT_TRUE(req->m_frames.m_bufs.empty()); + ASSERT_EQ(callbackCalled, 0); +} + } // namespace wpi::detail diff --git a/wpinet/src/test/native/cpp/WebSocketServerTest.cpp b/wpinet/src/test/native/cpp/WebSocketServerTest.cpp index 7d33fa2eec9..a051e553111 100644 --- a/wpinet/src/test/native/cpp/WebSocketServerTest.cpp +++ b/wpinet/src/test/native/cpp/WebSocketServerTest.cpp @@ -167,7 +167,7 @@ TEST_F(WebSocketServerTest, CloseReason) { ws->closed.connect([&](uint16_t code, std::string_view reason) { ++gotClosed; ASSERT_EQ(code, 1000); - ASSERT_EQ(reason, "hangup"); + ASSERT_EQ(reason, "remote close: hangup"); }); }; // need to respond with close for server to finish shutdown @@ -237,7 +237,7 @@ TEST_F(WebSocketServerTest, ReceiveCloseReason) { ws->closed.connect([&](uint16_t code, std::string_view reason) { ++gotClosed; ASSERT_EQ(code, 1000); - ASSERT_EQ(reason, "hangup"); + ASSERT_EQ(reason, "remote close: hangup"); }); }; const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'}; diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Current.java b/wpiunits/src/main/java/edu/wpi/first/units/Current.java index e1153fd87c4..744164c016c 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Current.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Current.java @@ -23,6 +23,17 @@ public class Current extends Unit { super(Current.class, toBaseConverter, fromBaseConverter, name, symbol); } + /** + * Constructs a unit of power equivalent to this unit of electrical current multiplied by another + * unit of voltage. For example, {@code Amps.times(Volts)} will return a unit of power equivalent + * to one Watt; {@code Amps.times(Millivolts)} will return a unit of power equivalent to a + * milliwatt, and so on. + * + * @param voltage the voltage unit to multiply by + * @param name the name of the resulting unit of power + * @param symbol the symbol used to represent the unit of power + * @return the power unit + */ public Power times(Unit voltage, String name, String symbol) { return new Power(this.toBaseUnits(1) * voltage.toBaseUnits(1), name, symbol); } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Dimensionless.java b/wpiunits/src/main/java/edu/wpi/first/units/Dimensionless.java index 5d1772f79b4..4fdef64f401 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Dimensionless.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Dimensionless.java @@ -15,7 +15,7 @@ public class Dimensionless extends Unit { * @param name the name of the unit * @param symbol the symbol of the unit */ - protected Dimensionless(double baseUnitEquivalent, String name, String symbol) { + Dimensionless(double baseUnitEquivalent, String name, String symbol) { super(Dimensionless.class, baseUnitEquivalent, name, symbol); } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Energy.java b/wpiunits/src/main/java/edu/wpi/first/units/Energy.java index d3513d50704..55a9eaf62d5 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Energy.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Energy.java @@ -14,12 +14,12 @@ * {@link Units} class. */ public class Energy extends Unit { - protected Energy( + Energy( UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) { super(Energy.class, toBaseConverter, fromBaseConverter, name, symbol); } - protected Energy(double baseUnitEquivalent, String name, String symbol) { + Energy(double baseUnitEquivalent, String name, String symbol) { super(Energy.class, baseUnitEquivalent, name, symbol); } } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Measure.java b/wpiunits/src/main/java/edu/wpi/first/units/Measure.java index 8982e4638df..3d776597047 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Measure.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Measure.java @@ -260,10 +260,9 @@ default boolean isNear(Measure other, double varianceThreshold) { } // abs so negative inputs are calculated correctly - var allowedVariance = Math.abs(varianceThreshold); + var tolerance = Math.abs(other.baseUnitMagnitude() * varianceThreshold); - return other.baseUnitMagnitude() * (1 - allowedVariance) <= this.baseUnitMagnitude() - && other.baseUnitMagnitude() * (1 + allowedVariance) >= this.baseUnitMagnitude(); + return Math.abs(this.baseUnitMagnitude() - other.baseUnitMagnitude()) <= tolerance; } /** diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Mult.java b/wpiunits/src/main/java/edu/wpi/first/units/Mult.java index 8a0a5fd2de7..9a8c33a34c5 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Mult.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Mult.java @@ -8,7 +8,9 @@ import java.util.Objects; /** - * A combinatory unit type that is equivalent to the product of two other others. + * A combinatory unit type that is equivalent to the product of two other others. Note that + * algebraic reduction is not possible in Java's generic type system, so {@code Mult} is not + * type-compatible with {@code Mult}! * * @param the type of the first unit in the result * @param the type of the second unit in the result @@ -20,6 +22,14 @@ public class Mult, B extends Unit> extends Unit> @SuppressWarnings("rawtypes") private static final LongToObjectHashMap cache = new LongToObjectHashMap<>(); + /** + * Creates a new product unit. Consider using {@link #combine} instead of manually calling this + * constructor. + * + * @param baseType the base type representing the unit product + * @param a the first unit of the product + * @param b the second unit of the product + */ protected Mult(Class> baseType, A a, B b) { super( baseType, @@ -48,7 +58,7 @@ protected Mult(Class> baseType, A a, B b) { */ @SuppressWarnings({"unchecked", "rawtypes"}) public static , B extends Unit> Mult combine(A a, B b) { - final long key = ((long) a.hashCode()) << 32L | ((long) b.hashCode()) & 0xFFFFFFFFL; + final long key = ((long) a.hashCode()) << 32L | (((long) b.hashCode()) & 0xFFFFFFFFL); if (cache.containsKey(key)) { return cache.get(key); } @@ -58,10 +68,20 @@ public static , B extends Unit> Mult combine(A a, B b return mult; } + /** + * Gets the first unit of the product. + * + * @return the first unit + */ public A unitA() { return m_unitA; } + /** + * Gets the second unit of the product. + * + * @return the second unit + */ public B unitB() { return m_unitB; } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Per.java b/wpiunits/src/main/java/edu/wpi/first/units/Per.java index e22b7d6caa3..cdb2fe37dbc 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Per.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Per.java @@ -11,7 +11,8 @@ * Generic combinatory unit type that represents the proportion of one unit to another, such as * Meters per Second or Radians per Celsius. * - *

Note: {@link Velocity} is used to represent the velocity dimension. + *

Note: {@link Velocity} is used to represent the velocity dimension, rather than {@code + * Per}. * * @param the type of the numerator unit * @param the type of the denominator unit @@ -27,6 +28,14 @@ public class Per, D extends Unit> extends Unit> { @SuppressWarnings("rawtypes") private static final LongToObjectHashMap cache = new LongToObjectHashMap<>(); + /** + * Creates a new proportional unit derived from the ratio of one unit to another. Consider using + * {@link #combine} instead of manually calling this constructor. + * + * @param baseType the base type representing the unit ratio + * @param numerator the numerator unit + * @param denominator the denominator unit + */ protected Per(Class> baseType, N numerator, D denominator) { super( baseType, @@ -58,7 +67,7 @@ protected Per(Class> baseType, N numerator, D denominator) { public static , D extends Unit> Per combine( N numerator, D denominator) { final long key = - ((long) numerator.hashCode()) << 32L | ((long) denominator.hashCode()) & 0xFFFFFFFFL; + ((long) numerator.hashCode()) << 32L | (((long) denominator.hashCode()) & 0xFFFFFFFFL); var existing = cache.get(key); if (existing != null) { @@ -70,10 +79,20 @@ public static , D extends Unit> Per combine( return newUnit; } + /** + * Gets the numerator unit. For a {@code Per}, this will return the {@code A} unit. + * + * @return the numerator unit + */ public N numerator() { return m_numerator; } + /** + * Gets the denominator unit. For a {@code Per}, this will return the {@code B} unit. + * + * @return the denominator unit + */ public D denominator() { return m_denominator; } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/UnaryFunction.java b/wpiunits/src/main/java/edu/wpi/first/units/UnaryFunction.java index ca5c8a2f7f3..a01ecfec91d 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/UnaryFunction.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/UnaryFunction.java @@ -6,6 +6,13 @@ import java.util.Objects; +/** + * A function that accepts a single {@code double} and returns a {@code double} result. This is used + * to represent arbitrary mapping functions for converting units to and from a base unit + * representation. Temperature units, in particular, typically have an offset from a value in Kelvin + * and may have a multiplication factor added in, which means that units cannot always be + * represented as simple ratios of their base units. + */ @FunctionalInterface public interface UnaryFunction { /** diff --git a/wpiunits/src/main/java/edu/wpi/first/units/UnitBuilder.java b/wpiunits/src/main/java/edu/wpi/first/units/UnitBuilder.java index b8dc0b9a6dd..a1831979da7 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/UnitBuilder.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/UnitBuilder.java @@ -20,6 +20,12 @@ public final class UnitBuilder> { private String m_name; private String m_symbol; + /** + * Creates a new unit builder object, building off of a base unit. The base unit does not have to + * be the base unit of its unit system; furlongs work just as well here as meters. + * + * @param base the unit to base the new unit off of + */ public UnitBuilder(U base) { this.m_base = Objects.requireNonNull(base, "Base unit cannot be null"); } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Units.java b/wpiunits/src/main/java/edu/wpi/first/units/Units.java index bf0ebd606bc..4652763273a 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Units.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Units.java @@ -14,91 +14,337 @@ private Units() { // Pseudo-classes describing the more common units of measure. + /** + * Used as an internal placeholder value when a specific unit type cannot be determined. Do not + * use this directly. + */ @SuppressWarnings("rawtypes") public static final Unit AnonymousBaseUnit = new Dimensionless(1, "", ""); // Distance + /** The base unit of distance. */ public static final Distance Meters = BaseUnits.Distance; + + /** The base unit of distance. */ + public static final Distance Meter = Meters; // alias + + /** 1/1000 of a {@link #Meter}. */ public static final Distance Millimeters = Milli(Meters, "Millimeter", "mm"); + + /** 1/1000 of a {@link #Meter}. */ + public static final Distance Millimeter = Millimeters; // alias + + /** 1/100 of a {@link #Meter}. */ public static final Distance Centimeters = derive(Meters).splitInto(100).named("Centimeter").symbol("cm").make(); + + /** 1/100 of a {@link #Meter}. */ + public static final Distance Centimeter = Centimeters; // alias + + /** 25.4/1000 of a {@link #Meter} and 1/12 of a {@link #Foot}. */ public static final Distance Inches = derive(Millimeters).aggregate(25.4).named("Inch").symbol("in").make(); + + /** 25.4/1000 of a {@link #Meter} and 1/12 of a {@link #Foot}. */ + public static final Distance Inch = Inches; // alias + + /** 304.8/1000 of a {@link #Meter}, or 12 {@link #Inches}. */ public static final Distance Feet = derive(Inches).aggregate(12).named("Foot").symbol("ft").make(); + /** 304.8/1000 of a {@link #Meter}, or 12 {@link #Inches}. */ + public static final Distance Foot = Feet; // alias + // Time + /** The base unit of time. */ public static final Time Seconds = BaseUnits.Time; + + /** Alias for {@link #Seconds} to make combined unit definitions read more smoothly. */ public static final Time Second = Seconds; // singularized alias + + /** 1/1000 of a {@link #Seconds Second}. */ public static final Time Milliseconds = Milli(Seconds); + + /** Alias for {@link #Milliseconds} to make combined unit definitions read more smoothly. */ public static final Time Millisecond = Milliseconds; // singularized alias + + /** 1/1,000,000 of a {@link #Seconds Second}. */ public static final Time Microseconds = Micro(Seconds); + + /** Alias for {@link #Microseconds} to make combined unit definitions read more smoothly. */ public static final Time Microsecond = Microseconds; // singularized alias + + /** 60 {@link #Seconds}. */ public static final Time Minutes = derive(Seconds).aggregate(60).named("Minute").symbol("min").make(); + + /** Alias for {@link #Minutes} to make combined unit definitions read more smoothly. */ public static final Time Minute = Minutes; // singularized alias // Angle + /** + * The base SI unit of angle, represented by the distance that the radius of a unit circle can + * wrap around its circumference. + */ public static final Angle Radians = BaseUnits.Angle; + + /** + * The base SI unit of angle, represented by the distance that the radius of a unit circle can + * wrap around its circumference. + */ + public static final Angle Radian = Radians; // alias + + /** + * A single turn of an object around an external axis. Numerically equivalent to {@link + * #Rotations}, but may be semantically more expressive in certain scenarios. + */ public static final Angle Revolutions = new Angle(2 * Math.PI, "Revolution", "R"); + + /** + * A single turn of an object around an external axis. Numerically equivalent to a {@link + * #Rotation}, but may be semantically more expressive in certain scenarios. + */ + public static final Angle Revolution = Revolutions; // alias + + /** + * A single turn of an object around an internal axis. Numerically equivalent to {@link + * #Revolutions}, but may be semantically more expressive in certain scenarios. + */ public static final Angle Rotations = new Angle(2 * Math.PI, "Rotation", "R"); // alias revolution + + /** + * A single turn of an object around an internal axis. Numerically equivalent to a {@link + * #Revolution}, but may be semantically more expressive in certain scenarios. + */ + public static final Angle Rotation = Rotations; // alias + + /** 1/360 of a turn around a circle, or 1/57.3 {@link #Radians}. */ public static final Angle Degrees = derive(Revolutions).splitInto(360).named("Degree").symbol("°").make(); + /** 1/360 of a turn around a circle, or 1/57.3 {@link #Radians}. */ + public static final Angle Degree = Degrees; // alias + // Velocity + /** + * The standard SI unit of linear velocity, equivalent to travelling at a rate of one {@link + * #Meters Meter} per {@link #Second}. + */ public static final Velocity MetersPerSecond = Meters.per(Second); + + /** + * A unit of linear velocity equivalent to travelling at a rate one {@link #Feet Foot} per {@link + * #Second}. + */ public static final Velocity FeetPerSecond = Feet.per(Second); + + /** + * A unit of linear velocity equivalent to travelling at a rate of one {@link #Inches Inch} per + * {@link #Second}. + */ public static final Velocity InchesPerSecond = Inches.per(Second); + /** + * A unit of angular velocity equivalent to spinning at a rate of one {@link #Revolutions + * Revolution} per {@link #Second}. + */ public static final Velocity RevolutionsPerSecond = Revolutions.per(Second); + + /** + * A unit of angular velocity equivalent to spinning at a rate of one {@link #Rotations Rotation} + * per {@link #Second}. + */ public static final Velocity RotationsPerSecond = Rotations.per(Second); + + /** + * A unit of angular velocity equivalent to spinning at a rate of one {@link #Rotations Rotation} + * per {@link #Minute}. Motor spec sheets often list maximum speeds in terms of RPM. + */ public static final Velocity RPM = Rotations.per(Minute); + + /** + * The standard SI unit of angular velocity, equivalent to spinning at a rate of one {@link + * #Radians Radian} per {@link #Second}. + */ public static final Velocity RadiansPerSecond = Radians.per(Second); + + /** + * A unit of angular velocity equivalent to spinning at a rate of one {@link #Degrees Degree} per + * {@link #Second}. + */ public static final Velocity DegreesPerSecond = Degrees.per(Second); // Acceleration + /** + * The standard SI unit of linear acceleration, equivalent to accelerating at a rate of one {@link + * #Meters Meter} per {@link #Second} every second. + */ public static final Velocity> MetersPerSecondPerSecond = MetersPerSecond.per(Second); + + /** + * A unit of acceleration equivalent to the pull of gravity on an object at sea level on Earth. + */ public static final Velocity> Gs = derive(MetersPerSecondPerSecond).aggregate(9.80665).named("G").symbol("G").make(); // Mass + /** The base SI unit of mass. */ public static final Mass Kilograms = BaseUnits.Mass; + + /** The base SI unit of mass. */ + public static final Mass Kilogram = Kilograms; // alias + + /** 1/1000 of a {@link #Kilogram}. */ public static final Mass Grams = Milli(Kilograms, "Gram", "g"); + + /** 1/1000 of a {@link #Kilogram}. */ + public static final Mass Gram = Grams; // alias + + /** + * A unit of mass equivalent to approximately 453 {@link #Grams}. This is not equivalent to + * pounds-force, which is the amount of force required to accelerate an object with one pound of + * mass at a rate of one {@link #Gs G}. + */ public static final Mass Pounds = derive(Grams).aggregate(453.592).named("Pound").symbol("lb.").make(); + + /** + * A unit of mass equivalent to approximately 453 {@link #Grams}. This is not equivalent to + * pounds-force, which is the amount of force required to accelerate an object with one pound of + * mass at a rate of one {@link #Gs G}. + */ + public static final Mass Pound = Pounds; // alias + + /** 1/16 of a {@link #Pound}. */ public static final Mass Ounces = derive(Pounds).splitInto(16).named("Ounce").symbol("oz.").make(); + /** 1/16 of a {@link #Pound}. */ + public static final Mass Ounce = Ounces; // alias + // Unitless + /** A dimensionless unit that performs no scaling whatsoever. */ public static final Dimensionless Value = BaseUnits.Value; + + /** + * A dimensionless unit equal to to 1/100th of a {@link #Value}. A measurement of {@code + * Percent.of(42)} would be equivalent to {@code Value.of(0.42)}. + */ public static final Dimensionless Percent = derive(Value).splitInto(100).named("Percent").symbol("%").make(); // Voltage + /** The base unit of electric potential. */ public static final Voltage Volts = BaseUnits.Voltage; + + /** The base unit of electric potential. */ + public static final Voltage Volt = Volts; // alias + + /** + * 1/1000 of a {@link #Volt}. Useful when dealing with low-voltage applications like LED drivers + * or low-power circuits. + */ public static final Voltage Millivolts = Milli(Volts); + /** + * 1/1000 of a {@link #Volt}. Useful when dealing with low-voltage applications like LED drivers + * or low-power circuits. + */ + public static final Voltage Millivolt = Millivolts; // alias + // Current + /** The base unit of electrical current. */ public static final Current Amps = BaseUnits.Current; + + /** The base unit of electrical current. */ + public static final Current Amp = Amps; // alias + + /** + * A unit equal to 1/1000 of an {@link #Amp}. Useful when dealing with low-current applications + * like LED drivers or low-power circuits. + */ public static final Current Milliamps = Milli(Amps); + /** + * A unit equal to 1/1000 of an {@link #Amp}. Useful when dealing with low-current applications + * like LED drivers or low-power circuits. + */ + public static final Current Milliamp = Milliamps; // alias + // Energy + /** The base unit of energy. */ public static final Energy Joules = BaseUnits.Energy; + + /** The base unit of energy. */ + public static final Energy Joule = Joules; // alias + + /** + * A unit equal to 1/1000 of a {@link #Joule}. Useful when dealing with lower-power applications. + */ public static final Energy Millijoules = Milli(Joules); + + /** + * A unit equal to 1/1000 of a {@link #Joule}. Useful when dealing with lower-power applications. + */ + public static final Energy Millijoule = Millijoules; // alias + + /** + * A unit equal to 1,000 {@link #Joules}. Useful when dealing with higher-level robot energy + * usage. + */ public static final Energy Kilojoules = Kilo(Joules); + /** + * A unit equal to 1,000 {@link #Joules}. Useful when dealing with higher-level robot energy + * usage. + */ + public static final Energy Kilojoule = Kilojoules; // alias + // Power + /** The base unit of power. Equivalent to one {@link #Joule} per {@link #Second}. */ public static final Power Watts = BaseUnits.Power; + + /** The base unit of power. Equivalent to one {@link #Joule} per {@link #Second}. */ + public static final Power Watt = Watts; // alias + + /** + * A unit equal to 1/1000 of a {@link #Watt}. Useful when dealing with lower-power applications. + */ public static final Power Milliwatts = Milli(Watts); + + /** + * A unit equal to 1/1000 of a {@link #Watt}. Useful when dealing with lower-power applications. + */ + public static final Power Milliwatt = Milliwatts; // alias + + /** + * A unit equal to 745.7 {@link #Watts}. May be useful when dealing with high-power gearboxes and + * motors. + */ public static final Power Horsepower = derive(Watts).aggregate(745.7).named("Horsepower").symbol("HP").make(); // Temperature + /** + * The base unit of temperature, where a value of 0 corresponds with absolutely zero energy in the + * measured system. Not particularly useful for robots unless you're cooling motors with liquid + * helium. + */ public static final Temperature Kelvin = BaseUnits.Temperature; + + /** + * The base SI unit of temperature, where a value of 0 roughly corresponds to the freezing point + * of water and a value of 100 corresponds to the boiling point. Electronics tend to exhibit + * degraded performance or damage above 90 degrees Celsius. + */ public static final Temperature Celsius = derive(Kelvin).offset(+273.15).named("Celsius").symbol("°C").make(); + /** + * The base imperial (American) unit of temperature, where a value of 32 roughly corresponds to + * the freezing point of water and a value of 212 corresponds to the boiling point. + */ public static final Temperature Fahrenheit = derive(Celsius) .mappingInputRange(0, 100) @@ -109,13 +355,31 @@ private Units() { // Standard feedforward units for kV and kA. // kS and kG are just volts, which is already defined earlier + /** + * A standard unit for measuring linear mechanisms' feedforward voltages based on a model of the + * system and a desired commanded linear velocity. + */ public static final Per> VoltsPerMeterPerSecond = Volts.per(MetersPerSecond); + + /** + * A standard unit for measuring linear mechanisms' feedforward voltages based on a model of the + * system and a desired commanded linear acceleration. + */ public static final Per>> VoltsPerMeterPerSecondSquared = Volts.per(MetersPerSecondPerSecond); + /** + * A standard unit for measuring angular mechanisms' feedforward voltages based on a model of the + * system and a desired commanded angular velocity. + */ public static final Per> VoltsPerRadianPerSecond = Volts.per(RadiansPerSecond); + + /** + * A standard unit for measuring angular mechanisms' feedforward voltages based on a model of the + * system and a desired commanded angular acceleration. + */ public static final Per>> VoltsPerRadianPerSecondSquared = Volts.per(RadiansPerSecond.per(Second)); @@ -201,11 +465,26 @@ public static > U Kilo(Unit baseUnit) { baseUnit, "Kilo" + baseUnit.name().toLowerCase(Locale.ROOT), "K" + baseUnit.symbol()); } + /** + * Creates a new unit builder object based on a given input unit. The builder can be used to + * fluently describe a new unit in terms of its relation to the base unit. + * + * @param unit the base unit from which to derive a new unit + * @param the dimension of the unit to derive + * @return a builder object + */ @SuppressWarnings("unchecked") public static > UnitBuilder derive(Unit unit) { return new UnitBuilder<>((U) unit); } + /** + * Returns an anonymous unit for use when a specific unit type is not known. Do not use this + * directly. + * + * @param the dimension of the desired anonymous unit + * @return the anonymous unit + */ @SuppressWarnings("unchecked") public static > U anonymous() { return (U) AnonymousBaseUnit; diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Velocity.java b/wpiunits/src/main/java/edu/wpi/first/units/Velocity.java index 185c59e6254..ec4e612006d 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Velocity.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Velocity.java @@ -33,7 +33,7 @@ public class Velocity> extends Unit> { /** Generates a cache key used for cache lookups. */ private static long cacheKey(Unit numerator, Unit denominator) { - return ((long) numerator.hashCode()) << 32L | ((long) denominator.hashCode()) & 0xFFFFFFFFL; + return ((long) numerator.hashCode()) << 32L | (((long) denominator.hashCode()) & 0xFFFFFFFFL); } /** diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Voltage.java b/wpiunits/src/main/java/edu/wpi/first/units/Voltage.java index 9006d19866f..d1646e1ee0d 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Voltage.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Voltage.java @@ -23,6 +23,17 @@ public class Voltage extends Unit { super(Voltage.class, toBaseConverter, fromBaseConverter, name, symbol); } + /** + * Constructs a unit of power equivalent to this unit of voltage multiplied by another unit of + * electrical current. For example, {@code Volts.times(Amps)} will return a unit of power + * equivalent to one Watt; {@code Volts.times(Milliams)} will return a unit of power equivalent to + * a milliwatt, and so on. + * + * @param current the current unit to multiply by + * @param name the name of the resulting unit of power + * @param symbol the symbol used to represent the unit of power + * @return the power unit + */ public Power times(Unit current, String name, String symbol) { return new Power(toBaseUnits(1) * current.toBaseUnits(1), name, symbol); } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/collections/LongToObjectHashMap.java b/wpiunits/src/main/java/edu/wpi/first/units/collections/LongToObjectHashMap.java index ff2e94db89a..2a18d7d6dec 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/collections/LongToObjectHashMap.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/collections/LongToObjectHashMap.java @@ -54,6 +54,9 @@ public class LongToObjectHashMap { @SuppressWarnings("unchecked") private V[] m_values = (V[]) new Object[m_capacity]; + /** Default constructor. */ + public LongToObjectHashMap() {} + /** * Puts a value {@code value} corresponding to key {@code key} in the map. * @@ -222,8 +225,19 @@ public Collection values() { return List.copyOf(values); // return a readonly copy } + /** + * Interface for map iterator function. + * + * @param Value type. + */ @FunctionalInterface public interface IteratorFunction { + /** + * Accepts a key-value pair from the map. + * + * @param key The key. + * @param value The value. + */ void accept(long key, V value); } diff --git a/wpiunits/src/test/java/edu/wpi/first/units/MeasureTest.java b/wpiunits/src/test/java/edu/wpi/first/units/MeasureTest.java index b2c729e3ece..31be4ca5816 100644 --- a/wpiunits/src/test/java/edu/wpi/first/units/MeasureTest.java +++ b/wpiunits/src/test/java/edu/wpi/first/units/MeasureTest.java @@ -173,7 +173,7 @@ void testToLongString() { var measure = Units.Volts.of(343); assertEquals("343.0 Volt", measure.toLongString()); assertEquals("343.0001 Volt", Units.Volts.of(343.0001).toLongString()); - assertEquals("1.2345678912345679E8 Volt", Units.Volts.of(123456789.123456789).toLongString()); + assertEquals("1.2345678912345678E8 Volt", Units.Volts.of(123456789.12345678).toLongString()); } @Test @@ -319,5 +319,29 @@ void testIsNear() { assertFalse(measureA.isNear(measureB, 0.739370)); assertTrue(measureA.isNear(measureB, 0.739375)); assertTrue(measureA.isNear(measureB, 100)); // some stupidly large range +/- 10000% + + var measureC = unit.of(-1.21); + var measureD = unit.ofBaseUnits(-64); + + assertTrue(measureC.isNear(measureC, 0)); + assertTrue(measureD.isNear(measureD, 0)); + + assertFalse(measureC.isNear(measureD, 0)); + assertFalse(measureC.isNear(measureD, 0.50)); + assertFalse(measureC.isNear(measureD, 0.739370)); + assertTrue(measureC.isNear(measureD, 0.739375)); + assertTrue(measureC.isNear(measureD, 100)); // some stupidly large range +/- 10000% + + var measureE = Units.Meters.of(1); + var measureF = Units.Feet.of(-3.28084); + + assertTrue(measureE.isNear(measureF, 2.01)); + assertFalse(measureE.isNear(measureF, 1.99)); + + assertTrue(measureF.isNear(measureE, 2.01)); + assertFalse(measureF.isNear(measureE, 1.99)); + + assertTrue(Units.Feet.zero().isNear(Units.Millimeters.zero(), 0.001)); + assertFalse(Units.Feet.of(2).isNear(Units.Millimeters.of(0), 0.001)); } } diff --git a/wpiutil/.styleguide b/wpiutil/.styleguide index 4932e16e051..0fbb58ab6db 100644 --- a/wpiutil/.styleguide +++ b/wpiutil/.styleguide @@ -3,6 +3,7 @@ cppHeaderFileInclude { \.hpp$ \.inc$ \.inl$ + expected$ math$ numbers$ scope$ diff --git a/wpiutil/CMakeLists.txt b/wpiutil/CMakeLists.txt index a3f6f1935bc..4caa3521bce 100644 --- a/wpiutil/CMakeLists.txt +++ b/wpiutil/CMakeLists.txt @@ -197,6 +197,17 @@ else() endif() endif() +install( + DIRECTORY src/main/native/thirdparty/expected/include/ + DESTINATION "${include_dest}/wpiutil" +) +target_include_directories( + wpiutil + PUBLIC + $ + $ + $ +) install(DIRECTORY src/main/native/thirdparty/memory/include/ DESTINATION "${include_dest}/wpiutil") target_include_directories( wpiutil @@ -270,4 +281,7 @@ if(WITH_TESTS) wpilib_add_test(wpiutil src/test/native/cpp) target_link_libraries(wpiutil_test wpiutil gmock_main wpiutil_testlib) + if(MSVC) + target_compile_options(wpiutil_test PRIVATE /utf-8) + endif() endif() diff --git a/wpiutil/build.gradle b/wpiutil/build.gradle index 7e4cc169907..941fe2ead36 100644 --- a/wpiutil/build.gradle +++ b/wpiutil/build.gradle @@ -180,6 +180,9 @@ nativeUtils.exportsConfigs { } cppHeadersZip { + from('src/main/native/thirdparty/expected/include') { + into '/' + } from('src/main/native/thirdparty/fmtlib/include') { into '/' } @@ -232,7 +235,7 @@ model { all { it.sources.each { it.exportedHeaders { - srcDirs 'src/main/native/include', 'src/main/native/thirdparty/fmtlib/include', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/sigslot/include', 'src/main/native/thirdparty/json/include', 'src/main/native/thirdparty/memory/include', 'src/main/native/thirdparty/mpack/include', 'src/main/native/thirdparty/protobuf/include' + srcDirs 'src/main/native/include', 'src/main/native/thirdparty/expected/include', 'src/main/native/thirdparty/fmtlib/include', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/sigslot/include', 'src/main/native/thirdparty/json/include', 'src/main/native/thirdparty/memory/include', 'src/main/native/thirdparty/mpack/include', 'src/main/native/thirdparty/protobuf/include' } } } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/CircularBuffer.java b/wpiutil/src/main/java/edu/wpi/first/util/CircularBuffer.java index 6041e44253e..e99bc2b237b 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/CircularBuffer.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/CircularBuffer.java @@ -4,7 +4,11 @@ package edu.wpi.first.util; -/** This is a simple circular buffer so we don't need to "bucket brigade" copy old values. */ +/** + * This is a simple circular buffer so we don't need to "bucket brigade" copy old values. + * + * @param Buffer element type. + */ public class CircularBuffer { private T[] m_data; @@ -17,7 +21,7 @@ public class CircularBuffer { /** * Create a CircularBuffer with the provided size. * - * @param size The size of the circular buffer. + * @param size Maximum number of buffer elements. */ @SuppressWarnings("unchecked") public CircularBuffer(int size) { @@ -49,7 +53,6 @@ public T getFirst() { * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= * size()) */ - @SuppressWarnings("unchecked") public T getLast() { // If there are no elements in the buffer, do nothing if (m_length == 0) { @@ -107,7 +110,6 @@ public void addLast(T value) { * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= * size()) */ - @SuppressWarnings("unchecked") public T removeFirst() { // If there are no elements in the buffer, do nothing if (m_length == 0) { @@ -127,7 +129,6 @@ public T removeFirst() { * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= * size()) */ - @SuppressWarnings("unchecked") public T removeLast() { // If there are no elements in the buffer, do nothing if (m_length == 0) { diff --git a/wpiutil/src/main/java/edu/wpi/first/util/CombinedRuntimeLoader.java b/wpiutil/src/main/java/edu/wpi/first/util/CombinedRuntimeLoader.java index 23149bd3817..8fe4839d25e 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/CombinedRuntimeLoader.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/CombinedRuntimeLoader.java @@ -16,11 +16,17 @@ import java.util.Map; import java.util.Objects; +/** Loads dynamic libraries for all platforms. */ public final class CombinedRuntimeLoader { private CombinedRuntimeLoader() {} private static String extractionDirectory; + /** + * Returns library extraction directory. + * + * @return Library extraction directory. + */ public static synchronized String getExtractionDirectory() { return extractionDirectory; } @@ -29,6 +35,12 @@ private static synchronized void setExtractionDirectory(String directory) { extractionDirectory = directory; } + /** + * Sets DLL directory. + * + * @param directory Directory. + * @return DLL directory. + */ public static native String setDllDirectory(String directory); private static String getLoadErrorMessage(String libraryName, UnsatisfiedLinkError ule) { diff --git a/wpiutil/src/main/java/edu/wpi/first/util/EventVector.java b/wpiutil/src/main/java/edu/wpi/first/util/EventVector.java index 4d2c80030a1..60fb5d13d09 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/EventVector.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/EventVector.java @@ -8,10 +8,14 @@ import java.util.List; import java.util.concurrent.locks.ReentrantLock; +/** A thread-safe container for handling events. */ public class EventVector { private final ReentrantLock m_lock = new ReentrantLock(); private final List m_events = new ArrayList<>(); + /** Default constructor. */ + public EventVector() {} + /** * Adds an event to the event vector. * diff --git a/wpiutil/src/main/java/edu/wpi/first/util/InterpolatingTreeMap.java b/wpiutil/src/main/java/edu/wpi/first/util/InterpolatingTreeMap.java index 2c54d00ca41..8efe9172fcc 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/InterpolatingTreeMap.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/InterpolatingTreeMap.java @@ -10,12 +10,17 @@ * Interpolating Tree Maps are used to get values at points that are not defined by making a guess * from points that are defined. This uses linear interpolation. * + * @param Key type. + * @param Value type. * @deprecated Use {@link edu.wpi.first.math.interpolation.InterpolatingDoubleTreeMap} instead */ @Deprecated(forRemoval = true, since = "2024") public class InterpolatingTreeMap { private final TreeMap m_map = new TreeMap<>(); + /** Default constructor. */ + public InterpolatingTreeMap() {} + /** * Inserts a key-value pair. * diff --git a/wpiutil/src/main/java/edu/wpi/first/util/RawFrame.java b/wpiutil/src/main/java/edu/wpi/first/util/RawFrame.java index bad2dcc7616..dd074bf3409 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/RawFrame.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/RawFrame.java @@ -17,7 +17,7 @@ public class RawFrame implements AutoCloseable { private int m_width; private int m_height; private int m_stride; - private PixelFormat m_pixelFormat; + private PixelFormat m_pixelFormat = PixelFormat.kUnknown; /** Construct a new empty RawFrame. */ public RawFrame() { @@ -102,7 +102,12 @@ public void setInfo(int width, int height, int stride, PixelFormat pixelFormat) m_stride = stride; m_pixelFormat = pixelFormat; WPIUtilJNI.setRawFrameInfo( - m_nativeObj, m_data.limit(), width, height, stride, pixelFormat.getValue()); + m_nativeObj, + m_data != null ? m_data.limit() : 0, + width, + height, + stride, + pixelFormat.getValue()); } /** @@ -142,7 +147,7 @@ public long getDataPtr() { * @return The total size of the data stored in the frame. */ public int getSize() { - return m_data.limit(); + return m_data != null ? m_data.limit() : 0; } /** diff --git a/wpiutil/src/main/java/edu/wpi/first/util/RuntimeDetector.java b/wpiutil/src/main/java/edu/wpi/first/util/RuntimeDetector.java index 550339ecade..72593dcafc2 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/RuntimeDetector.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/RuntimeDetector.java @@ -6,6 +6,9 @@ import java.io.File; +/** + * A utility class for detecting and providing platform-specific such as OS and CPU architecture. + */ public final class RuntimeDetector { private static String filePrefix; private static String fileExtension; @@ -131,32 +134,57 @@ public static boolean isArm32() { } /** - * check if architecture is Arm64. + * Check if architecture is Arm64. * - * @return if architecture is Arm64 + * @return if architecture is Arm64. */ public static boolean isArm64() { String arch = System.getProperty("os.arch"); return "aarch64".equals(arch) || "arm64".equals(arch); } + /** + * Check if OS is Linux. + * + * @return if OS is Linux. + */ public static boolean isLinux() { return System.getProperty("os.name").startsWith("Linux"); } + /** + * Check if OS is Windows. + * + * @return if OS is Windows. + */ public static boolean isWindows() { return System.getProperty("os.name").startsWith("Windows"); } + /** + * Check if OS is Mac. + * + * @return if OS is Mac. + */ public static boolean isMac() { return System.getProperty("os.name").startsWith("Mac"); } + /** + * Check if OS is 32bit Intel. + * + * @return if OS is 32bit Intel. + */ public static boolean is32BitIntel() { String arch = System.getProperty("os.arch"); return "x86".equals(arch) || "i386".equals(arch); } + /** + * Check if OS is 64bit Intel. + * + * @return if OS is 64bit Intel. + */ public static boolean is64BitIntel() { String arch = System.getProperty("os.arch"); return "amd64".equals(arch) || "x86_64".equals(arch); diff --git a/wpiutil/src/main/java/edu/wpi/first/util/RuntimeLoader.java b/wpiutil/src/main/java/edu/wpi/first/util/RuntimeLoader.java index f24ace3323f..474666ee02f 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/RuntimeLoader.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/RuntimeLoader.java @@ -17,6 +17,11 @@ import java.util.Locale; import java.util.Scanner; +/** + * Loads a native library at runtime. + * + * @param The class to load. + */ public final class RuntimeLoader { private static String defaultExtractionRoot; diff --git a/wpiutil/src/main/java/edu/wpi/first/util/WPISerializable.java b/wpiutil/src/main/java/edu/wpi/first/util/WPISerializable.java index 753f40d2078..200deb54960 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/WPISerializable.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/WPISerializable.java @@ -5,4 +5,4 @@ package edu.wpi.first.util; /** Marker interface to indicate a class is serializable using WPI serialization methods. */ -public interface WPISerializable {} +public interface WPISerializable {} diff --git a/wpiutil/src/main/java/edu/wpi/first/util/WPIUtilJNI.java b/wpiutil/src/main/java/edu/wpi/first/util/WPIUtilJNI.java index 12a0815e13b..0bd5b2169cf 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/WPIUtilJNI.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/WPIUtilJNI.java @@ -8,20 +8,35 @@ import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; +/** WPIUtil JNI. */ public class WPIUtilJNI { static boolean libraryLoaded = false; static RuntimeLoader 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 { @@ -55,30 +70,101 @@ public static synchronized void forceLoad() throws IOException { libraryLoaded = true; } + /** + * Write the given string to stderr. + * + * @param str String to write. + */ public static native void writeStderr(String str); + /** Enable mock time. */ public static native void enableMockTime(); + /** Disable mock time. */ public static native void disableMockTime(); + /** + * Set mock time. + * + * @param time The desired time in microseconds. + */ public static native void setMockTime(long time); + /** + * Returns the time. + * + * @return The time. + */ public static native long now(); + /** + * Returns the system time. + * + * @return The system time. + */ public static native long getSystemTime(); + /** + * Creates an event. Events have binary state (signaled or not signaled) and may be either + * automatically reset or manually reset. Automatic-reset events go to non-signaled state when a + * WaitForObject is woken up by the event; manual-reset events require ResetEvent() to be called + * to set the event to non-signaled state; if ResetEvent() is not called, any waiter on that event + * will immediately wake when called. + * + * @param manualReset true for manual reset, false for automatic reset + * @param initialState true to make the event initially in signaled state + * @return Event handle + */ public static native int createEvent(boolean manualReset, boolean initialState); + /** + * Destroys an event. Destruction wakes up any waiters. + * + * @param eventHandle event handle + */ public static native void destroyEvent(int eventHandle); + /** + * Sets an event to signaled state. + * + * @param eventHandle event handle + */ public static native void setEvent(int eventHandle); + /** + * Sets an event to non-signaled state. + * + * @param eventHandle event handle + */ public static native void resetEvent(int eventHandle); + /** + * Creates a semaphore. Semaphores keep an internal counter. Releasing the semaphore increases the + * count. A semaphore with a non-zero count is considered signaled. When a waiter wakes up it + * atomically decrements the count by 1. This is generally useful in a single-supplier, + * multiple-consumer scenario. + * + * @param initialCount initial value for the semaphore's internal counter + * @param maximumCount maximum value for the samephore's internal counter + * @return Semaphore handle + */ public static native int createSemaphore(int initialCount, int maximumCount); + /** + * Destroys a semaphore. Destruction wakes up any waiters. + * + * @param semHandle semaphore handle + */ public static native void destroySemaphore(int semHandle); + /** + * Releases N counts of a semaphore. + * + * @param semHandle semaphore handle + * @param releaseCount amount to add to semaphore's internal counter; must be positive + * @return True on successful release, false on failure (e.g. release count would exceed maximum + * value, or handle invalid) + */ public static native boolean releaseSemaphore(int semHandle, int releaseCount); static native long allocateRawFrame(); @@ -137,4 +223,7 @@ public static native boolean waitForObjectTimeout(int handle, double timeout) */ public static native int[] waitForObjectsTimeout(int[] handles, double timeout) throws InterruptedException; + + /** Utility class. */ + protected WPIUtilJNI() {} } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/cleanup/CleanupPool.java b/wpiutil/src/main/java/edu/wpi/first/util/cleanup/CleanupPool.java index 0ae5200e8c4..4b8c21115f2 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/cleanup/CleanupPool.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/cleanup/CleanupPool.java @@ -16,6 +16,9 @@ public class CleanupPool implements AutoCloseable { // state ArrayDeque is faster anyway. private final Deque m_closers = new ArrayDeque<>(); + /** Default constructor. */ + public CleanupPool() {} + /** * Registers an object in the object stack for cleanup. * diff --git a/wpiutil/src/main/java/edu/wpi/first/util/cleanup/SkipCleanup.java b/wpiutil/src/main/java/edu/wpi/first/util/cleanup/SkipCleanup.java index e2bc72eaeb6..fcbcb7e896c 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/cleanup/SkipCleanup.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/cleanup/SkipCleanup.java @@ -9,6 +9,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** Attribute for telling JVM to skip object cleanup. */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SkipCleanup {} diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanArrayLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanArrayLogEntry.java index 718d46029a1..21ac5963593 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanArrayLogEntry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanArrayLogEntry.java @@ -6,20 +6,49 @@ /** Log array of boolean values. */ public class BooleanArrayLogEntry extends DataLogEntry { + /** The data type for boolean array values. */ public static final String kDataType = "boolean[]"; + /** + * Constructs a boolean array log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param timestamp entry creation timestamp (0=now) + */ public BooleanArrayLogEntry(DataLog log, String name, String metadata, long timestamp) { super(log, name, kDataType, metadata, timestamp); } + /** + * Constructs a boolean array log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + */ public BooleanArrayLogEntry(DataLog log, String name, String metadata) { this(log, name, metadata, 0); } + /** + * Constructs a boolean array log entry. + * + * @param log datalog + * @param name name of the entry + * @param timestamp entry creation timestamp (0=now) + */ public BooleanArrayLogEntry(DataLog log, String name, long timestamp) { this(log, name, "", timestamp); } + /** + * Constructs a boolean array log entry. + * + * @param log datalog + * @param name name of the entry + */ public BooleanArrayLogEntry(DataLog log, String name) { this(log, name, 0); } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanLogEntry.java index 503b83bfd82..c413bfa42a7 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanLogEntry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanLogEntry.java @@ -6,20 +6,49 @@ /** Log boolean values. */ public class BooleanLogEntry extends DataLogEntry { + /** The data type for boolean values. */ public static final String kDataType = "boolean"; + /** + * Constructs a boolean log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param timestamp entry creation timestamp (0=now) + */ public BooleanLogEntry(DataLog log, String name, String metadata, long timestamp) { super(log, name, kDataType, metadata, timestamp); } + /** + * Constructs a boolean log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + */ public BooleanLogEntry(DataLog log, String name, String metadata) { this(log, name, metadata, 0); } + /** + * Constructs a boolean log entry. + * + * @param log datalog + * @param name name of the entry + * @param timestamp entry creation timestamp (0=now) + */ public BooleanLogEntry(DataLog log, String name, long timestamp) { this(log, name, "", timestamp); } + /** + * Constructs a boolean log entry. + * + * @param log datalog + * @param name name of the entry + */ public BooleanLogEntry(DataLog log, String name) { this(log, name, 0); } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLog.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLog.java index 97c629f6a24..f48b0815cf8 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLog.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLog.java @@ -494,6 +494,11 @@ public void appendStringArray(int entry, String[] arr, long timestamp) { DataLogJNI.appendStringArray(m_impl, entry, arr, timestamp); } + /** + * Gets the JNI implementation handle. + * + * @return data log handle. + */ public long getImpl() { return m_impl; } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogEntry.java index 4beaff2e443..8502428405e 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogEntry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogEntry.java @@ -6,15 +6,39 @@ /** Log entry base class. */ public class DataLogEntry { + /** + * Constructs a data log entry. + * + * @param log datalog + * @param name name of the entry + * @param type Data type + * @param metadata metadata + * @param timestamp entry creation timestamp (0=now) + */ protected DataLogEntry(DataLog log, String name, String type, String metadata, long timestamp) { m_log = log; m_entry = log.start(name, type, metadata, timestamp); } + /** + * Constructs a data log entry. + * + * @param log datalog + * @param name name of the entry + * @param type Data type + * @param metadata metadata + */ protected DataLogEntry(DataLog log, String name, String type, String metadata) { this(log, name, type, metadata, 0); } + /** + * Constructs a data log entry. + * + * @param log datalog + * @param name name of the entry + * @param type Data type + */ protected DataLogEntry(DataLog log, String name, String type) { this(log, name, type, ""); } @@ -52,6 +76,9 @@ public void finish() { finish(0); } + /** The data log instance associated with the entry. */ protected final DataLog m_log; + + /** The data log entry index. */ protected final int m_entry; } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogJNI.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogJNI.java index f94a86f057d..c7643390f12 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogJNI.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogJNI.java @@ -7,35 +7,145 @@ import edu.wpi.first.util.WPIUtilJNI; import java.nio.ByteBuffer; +/** + * DataLog wpiutil JNI Functions. + * + * @see "wpiutil/DataLog.h" + */ public class DataLogJNI extends WPIUtilJNI { + /** + * Create a new Data Log. The log will be initially created with a temporary filename. + * + * @param dir directory to store the log + * @param filename filename to use; if none provided, a random filename is generated of the form + * "wpilog_{}.wpilog" + * @param period time between automatic flushes to disk, in seconds; this is a time/storage + * tradeoff + * @param extraHeader extra header data + * @return data log implementation handle + */ static native long create(String dir, String filename, double period, String extraHeader); + /** + * Change log filename. + * + * @param impl data log implementation handle + * @param filename filename + */ static native void setFilename(long impl, String filename); + /** + * Explicitly flushes the log data to disk. + * + * @param impl data log implementation handle + */ static native void flush(long impl); + /** + * Pauses appending of data records to the log. While paused, no data records are saved (e.g. + * AppendX is a no-op). Has no effect on entry starts / finishes / metadata changes. + * + * @param impl data log implementation handle + */ static native void pause(long impl); + /** + * Resumes appending of data records to the log. If called after Stop(), opens a new file (with + * random name if SetFilename was not called after Stop()) and appends Start records and schema + * data values for all previously started entries and schemas. + * + * @param impl data log implementation handle + */ static native void resume(long impl); + /** + * Stops appending all records to the log, and closes the log file. + * + * @param impl data log implementation handle + */ static native void stop(long impl); + /** + * Registers a data schema. Data schemas provide information for how a certain data type string + * can be decoded. The type string of a data schema indicates the type of the schema itself (e.g. + * "protobuf" for protobuf schemas, "struct" for struct schemas, etc). In the data log, schemas + * are saved just like normal records, with the name being generated from the provided name: + * "/.schema/<name>". Duplicate calls to this function with the same name are silently + * ignored. + * + * @param impl data log implementation handle + * @param name Name (the string passed as the data type for records using this schema) + * @param type Type of schema (e.g. "protobuf", "struct", etc) + * @param schema Schema data + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void addSchema(long impl, String name, String type, byte[] schema, long timestamp); static native void addSchemaString( long impl, String name, String type, String schema, long timestamp); + /** + * Start an entry. Duplicate names are allowed (with the same type), and result in the same index + * being returned (Start/Finish are reference counted). A duplicate name with a different type + * will result in an error message being printed to the console and 0 being returned (which will + * be ignored by the Append functions). + * + * @param impl data log implementation handle + * @param name Name + * @param type Data type + * @param metadata Initial metadata (e.g. data properties) + * @param timestamp Time stamp (may be 0 to indicate now) + * @return Entry index + */ static native int start(long impl, String name, String type, String metadata, long timestamp); + /** + * Finish an entry. + * + * @param impl data log implementation handle + * @param entry Entry index + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void finish(long impl, int entry, long timestamp); + /** + * Updates the metadata for an entry. + * + * @param impl data log implementation handle + * @param entry Entry index + * @param metadata New metadata for the entry + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void setMetadata(long impl, int entry, String metadata, long timestamp); + /** + * Closes the data log implementation handle. + * + * @param impl data log implementation handle + */ static native void close(long impl); + /** + * Appends a raw record to the log. + * + * @param impl data log implementation handle + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param data Byte array to record + * @param len Length of byte array + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void appendRaw( long impl, int entry, byte[] data, int start, int len, long timestamp); + /** + * Appends a raw record to the log. + * + * @param impl data log implementation handle + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param data ByteBuffer to record + * @param len Length of byte array + * @param timestamp Time stamp (may be 0 to indicate now) + */ static void appendRaw(long impl, int entry, ByteBuffer data, int start, int len, long timestamp) { if (data.isDirect()) { if (start < 0) { @@ -58,23 +168,106 @@ static void appendRaw(long impl, int entry, ByteBuffer data, int start, int len, private static native void appendRawBuffer( long impl, int entry, ByteBuffer data, int start, int len, long timestamp); + /** + * Appends a boolean record to the log. + * + * @param impl data log implementation handle + * @param entry Entry index, as returned by Start() + * @param value Boolean value to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void appendBoolean(long impl, int entry, boolean value, long timestamp); + /** + * Appends an integer record to the log. + * + * @param impl data log implementation handle + * @param entry Entry index, as returned by Start() + * @param value Integer value to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void appendInteger(long impl, int entry, long value, long timestamp); + /** + * Appends a float record to the log. + * + * @param impl data log implementation handle + * @param entry Entry index, as returned by Start() + * @param value Float value to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void appendFloat(long impl, int entry, float value, long timestamp); + /** + * Appends a double record to the log. + * + * @param impl data log implementation handle + * @param entry Entry index, as returned by Start() + * @param value Double value to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void appendDouble(long impl, int entry, double value, long timestamp); + /** + * Appends a string record to the log. + * + * @param impl data log implementation handle + * @param entry Entry index, as returned by Start() + * @param value String value to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void appendString(long impl, int entry, String value, long timestamp); + /** + * Appends a boolean array record to the log. + * + * @param impl data log implementation handle + * @param entry Entry index, as returned by Start() + * @param arr Boolean array to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void appendBooleanArray(long impl, int entry, boolean[] value, long timestamp); + /** + * Appends an integer array record to the log. + * + * @param impl data log implementation handle + * @param entry Entry index, as returned by Start() + * @param arr Integer array to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void appendIntegerArray(long impl, int entry, long[] value, long timestamp); + /** + * Appends a float array record to the log. + * + * @param impl data log implementation handle + * @param entry Entry index, as returned by Start() + * @param arr Float array to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void appendFloatArray(long impl, int entry, float[] value, long timestamp); + /** + * Appends a double array record to the log. + * + * @param impl data log implementation handle + * @param entry Entry index, as returned by Start() + * @param arr Double array to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void appendDoubleArray(long impl, int entry, double[] value, long timestamp); + /** + * Appends a string array record to the log. + * + * @param impl data log implementation handle + * @param entry Entry index, as returned by Start() + * @param arr String array to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ static native void appendStringArray(long impl, int entry, String[] value, long timestamp); + + /** Utility class. */ + private DataLogJNI() {} } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleArrayLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleArrayLogEntry.java index 67ef8c377e7..485a9c8c82b 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleArrayLogEntry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleArrayLogEntry.java @@ -6,20 +6,49 @@ /** Log array of double values. */ public class DoubleArrayLogEntry extends DataLogEntry { + /** The data type for double array values. */ public static final String kDataType = "double[]"; + /** + * Constructs a double array log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param timestamp entry creation timestamp (0=now) + */ public DoubleArrayLogEntry(DataLog log, String name, String metadata, long timestamp) { super(log, name, kDataType, metadata, timestamp); } + /** + * Constructs a double array log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + */ public DoubleArrayLogEntry(DataLog log, String name, String metadata) { this(log, name, metadata, 0); } + /** + * Constructs a double array log entry. + * + * @param log datalog + * @param name name of the entry + * @param timestamp entry creation timestamp (0=now) + */ public DoubleArrayLogEntry(DataLog log, String name, long timestamp) { this(log, name, "", timestamp); } + /** + * Constructs a double array log entry. + * + * @param log datalog + * @param name name of the entry + */ public DoubleArrayLogEntry(DataLog log, String name) { this(log, name, 0); } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleLogEntry.java index f16c27e2403..a089df2cd8a 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleLogEntry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleLogEntry.java @@ -6,20 +6,49 @@ /** Log double values. */ public class DoubleLogEntry extends DataLogEntry { + /** The data type for double values. */ public static final String kDataType = "double"; + /** + * Constructs a double log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param timestamp entry creation timestamp (0=now) + */ public DoubleLogEntry(DataLog log, String name, String metadata, long timestamp) { super(log, name, kDataType, metadata, timestamp); } + /** + * Constructs a double log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + */ public DoubleLogEntry(DataLog log, String name, String metadata) { this(log, name, metadata, 0); } + /** + * Constructs a double log entry. + * + * @param log datalog + * @param name name of the entry + * @param timestamp entry creation timestamp (0=now) + */ public DoubleLogEntry(DataLog log, String name, long timestamp) { this(log, name, "", timestamp); } + /** + * Constructs a double log entry. + * + * @param log datalog + * @param name name of the entry + */ public DoubleLogEntry(DataLog log, String name) { this(log, name, 0); } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatArrayLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatArrayLogEntry.java index 3a0b7e087c0..be25970ffcd 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatArrayLogEntry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatArrayLogEntry.java @@ -6,20 +6,49 @@ /** Log array of float values. */ public class FloatArrayLogEntry extends DataLogEntry { + /** The data type for float array values. */ public static final String kDataType = "float[]"; + /** + * Constructs a float array log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param timestamp entry creation timestamp (0=now) + */ public FloatArrayLogEntry(DataLog log, String name, String metadata, long timestamp) { super(log, name, kDataType, metadata, timestamp); } + /** + * Constructs a float array log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + */ public FloatArrayLogEntry(DataLog log, String name, String metadata) { this(log, name, metadata, 0); } + /** + * Constructs a float array log entry. + * + * @param log datalog + * @param name name of the entry + * @param timestamp entry creation timestamp (0=now) + */ public FloatArrayLogEntry(DataLog log, String name, long timestamp) { this(log, name, "", timestamp); } + /** + * Constructs a float array log entry. + * + * @param log datalog + * @param name name of the entry + */ public FloatArrayLogEntry(DataLog log, String name) { this(log, name, 0); } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatLogEntry.java index 28adc3442f5..28f83cb6a60 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatLogEntry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatLogEntry.java @@ -6,20 +6,49 @@ /** Log float values. */ public class FloatLogEntry extends DataLogEntry { + /** The data type for float values. */ public static final String kDataType = "float"; + /** + * Constructs a float log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param timestamp entry creation timestamp (0=now) + */ public FloatLogEntry(DataLog log, String name, String metadata, long timestamp) { super(log, name, kDataType, metadata, timestamp); } + /** + * Constructs a float log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + */ public FloatLogEntry(DataLog log, String name, String metadata) { this(log, name, metadata, 0); } + /** + * Constructs a float log entry. + * + * @param log datalog + * @param name name of the entry + * @param timestamp entry creation timestamp (0=now) + */ public FloatLogEntry(DataLog log, String name, long timestamp) { this(log, name, "", timestamp); } + /** + * Constructs a float log entry. + * + * @param log datalog + * @param name name of the entry + */ public FloatLogEntry(DataLog log, String name) { this(log, name, 0); } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerArrayLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerArrayLogEntry.java index 2cffc8d3f5e..d2f8f0ea52f 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerArrayLogEntry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerArrayLogEntry.java @@ -6,20 +6,49 @@ /** Log array of integer values. */ public class IntegerArrayLogEntry extends DataLogEntry { + /** The data type for integer array values. */ public static final String kDataType = "int64[]"; + /** + * Constructs a integer array log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param timestamp entry creation timestamp (0=now) + */ public IntegerArrayLogEntry(DataLog log, String name, String metadata, long timestamp) { super(log, name, kDataType, metadata, timestamp); } + /** + * Constructs a integer array log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + */ public IntegerArrayLogEntry(DataLog log, String name, String metadata) { this(log, name, metadata, 0); } + /** + * Constructs a integer array log entry. + * + * @param log datalog + * @param name name of the entry + * @param timestamp entry creation timestamp (0=now) + */ public IntegerArrayLogEntry(DataLog log, String name, long timestamp) { this(log, name, "", timestamp); } + /** + * Constructs a integer array log entry. + * + * @param log datalog + * @param name name of the entry + */ public IntegerArrayLogEntry(DataLog log, String name) { this(log, name, 0); } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerLogEntry.java index 142ca5da61a..395a208f74c 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerLogEntry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerLogEntry.java @@ -6,20 +6,49 @@ /** Log integer values. */ public class IntegerLogEntry extends DataLogEntry { + /** The data type for integer values. */ public static final String kDataType = "int64"; + /** + * Constructs a integer log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param timestamp entry creation timestamp (0=now) + */ public IntegerLogEntry(DataLog log, String name, String metadata, long timestamp) { super(log, name, kDataType, metadata, timestamp); } + /** + * Constructs a integer log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + */ public IntegerLogEntry(DataLog log, String name, String metadata) { this(log, name, metadata, 0); } + /** + * Constructs a integer log entry. + * + * @param log datalog + * @param name name of the entry + * @param timestamp entry creation timestamp (0=now) + */ public IntegerLogEntry(DataLog log, String name, long timestamp) { this(log, name, "", timestamp); } + /** + * Constructs a integer log entry. + * + * @param log datalog + * @param name name of the entry + */ public IntegerLogEntry(DataLog log, String name) { this(log, name, 0); } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/RawLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/RawLogEntry.java index 972fc03cfae..a9e3373335a 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/RawLogEntry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/RawLogEntry.java @@ -8,28 +8,74 @@ /** Log raw byte array values. */ public class RawLogEntry extends DataLogEntry { + /** The data type for raw values. */ public static final String kDataType = "raw"; + /** + * Constructs a raw log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param type Data type + * @param timestamp entry creation timestamp (0=now) + */ public RawLogEntry(DataLog log, String name, String metadata, String type, long timestamp) { super(log, name, type, metadata, timestamp); } + /** + * Constructs a raw log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param type Data type + */ public RawLogEntry(DataLog log, String name, String metadata, String type) { this(log, name, metadata, type, 0); } + /** + * Constructs a raw log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param timestamp entry creation timestamp (0=now) + */ public RawLogEntry(DataLog log, String name, String metadata, long timestamp) { this(log, name, metadata, kDataType, timestamp); } + /** + * Constructs a raw log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + */ public RawLogEntry(DataLog log, String name, String metadata) { this(log, name, metadata, 0); } + /** + * Constructs a raw log entry. + * + * @param log datalog + * @param name name of the entry + * @param timestamp entry creation timestamp (0=now) + */ public RawLogEntry(DataLog log, String name, long timestamp) { this(log, name, "", timestamp); } + /** + * Constructs a raw log entry. + * + * @param log datalog + * @param name name of the entry + */ public RawLogEntry(DataLog log, String name) { this(log, name, 0); } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringArrayLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringArrayLogEntry.java index 37bdeb1b961..f0a6dde7377 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringArrayLogEntry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringArrayLogEntry.java @@ -6,20 +6,49 @@ /** Log array of string values. */ public class StringArrayLogEntry extends DataLogEntry { + /** The data type for string array values. */ public static final String kDataType = "string[]"; + /** + * Constructs a string array log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param timestamp entry creation timestamp (0=now) + */ public StringArrayLogEntry(DataLog log, String name, String metadata, long timestamp) { super(log, name, kDataType, metadata, timestamp); } + /** + * Constructs a string array log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + */ public StringArrayLogEntry(DataLog log, String name, String metadata) { this(log, name, metadata, 0); } + /** + * Constructs a string array log entry. + * + * @param log datalog + * @param name name of the entry + * @param timestamp entry creation timestamp (0=now) + */ public StringArrayLogEntry(DataLog log, String name, long timestamp) { this(log, name, "", timestamp); } + /** + * Constructs a string array log entry. + * + * @param log datalog + * @param name name of the entry + */ public StringArrayLogEntry(DataLog log, String name) { this(log, name, 0); } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringLogEntry.java index 0722dc03c9c..27c8aefef57 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringLogEntry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringLogEntry.java @@ -6,28 +6,74 @@ /** Log string values. */ public class StringLogEntry extends DataLogEntry { + /** The data type for string values. */ public static final String kDataType = "string"; + /** + * Constructs a String log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param type Data type + * @param timestamp entry creation timestamp (0=now) + */ public StringLogEntry(DataLog log, String name, String metadata, String type, long timestamp) { super(log, name, type, metadata, timestamp); } + /** + * Constructs a String log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param type Data type + */ public StringLogEntry(DataLog log, String name, String metadata, String type) { this(log, name, metadata, type, 0); } + /** + * Constructs a String log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + * @param timestamp entry creation timestamp (0=now) + */ public StringLogEntry(DataLog log, String name, String metadata, long timestamp) { this(log, name, metadata, kDataType, timestamp); } + /** + * Constructs a String log entry. + * + * @param log datalog + * @param name name of the entry + * @param metadata metadata + */ public StringLogEntry(DataLog log, String name, String metadata) { this(log, name, metadata, 0); } + /** + * Constructs a String log entry. + * + * @param log datalog + * @param name name of the entry + * @param timestamp entry creation timestamp (0=now) + */ public StringLogEntry(DataLog log, String name, long timestamp) { this(log, name, "", timestamp); } + /** + * Constructs a String log entry. + * + * @param log datalog + * @param name name of the entry + */ public StringLogEntry(DataLog log, String name) { this(log, name, 0); } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructArrayLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructArrayLogEntry.java index b3a31c9a52e..e208f2c0d0d 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructArrayLogEntry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructArrayLogEntry.java @@ -87,7 +87,7 @@ public static StructArrayLogEntry create(DataLog log, String name, Struct * @param nelem number of elements */ public void reserve(int nelem) { - synchronized (m_buf) { + synchronized (this) { m_buf.reserve(nelem); } } @@ -121,7 +121,7 @@ public void append(T[] value) { * @param timestamp Time stamp (0 to indicate now) */ public void append(Collection value, long timestamp) { - synchronized (m_buf) { + synchronized (this) { ByteBuffer bb = m_buf.writeArray(value); m_log.appendRaw(m_entry, bb, 0, bb.position(), timestamp); } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufBuffer.java b/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufBuffer.java index ae03c633002..1f8fdf090a2 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufBuffer.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufBuffer.java @@ -26,6 +26,14 @@ private ProtobufBuffer(Protobuf proto) { m_proto = proto; } + /** + * Creates a ProtobufBuffer for the given Protobuf object. + * + * @param The type to serialize. + * @param The Protobuf message type. + * @param proto The Protobuf object. + * @return A ProtobufBuffer for the given Protobuf object. + */ public static > ProtobufBuffer create( Protobuf proto) { return new ProtobufBuffer<>(proto); diff --git a/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufSerializable.java b/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufSerializable.java index 78fdf8fbff0..ac75065659c 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufSerializable.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufSerializable.java @@ -12,4 +12,4 @@ *

While this cannot be enforced by the interface, any class implementing this interface should * provide a public final static `proto` member variable. */ -public interface ProtobufSerializable extends WPISerializable {} +public interface ProtobufSerializable extends WPISerializable {} diff --git a/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java b/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java index db822ce441b..ee93725acd1 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java @@ -15,10 +15,14 @@ import java.util.function.LongSupplier; import java.util.function.Supplier; +/** Helper class for building Sendable dashboard representations. */ public interface SendableBuilder extends AutoCloseable { /** The backend kinds used for the sendable builder. */ enum BackendKind { + /** Unknown. */ kUnknown, + + /** NetworkTables. */ kNetworkTables } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableRegistry.java b/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableRegistry.java index 6f0ad41d656..025b802094d 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableRegistry.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableRegistry.java @@ -506,6 +506,9 @@ public static class CallbackData { /** Sendable builder for the sendable. */ public SendableBuilder builder; + + /** Default constructor. */ + public CallbackData() {} } // As foreachLiveWindow is single threaded, cache the components it diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/BadSchemaException.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/BadSchemaException.java index 6ff22369615..9c69e1f6659 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/struct/BadSchemaException.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/BadSchemaException.java @@ -4,34 +4,70 @@ package edu.wpi.first.util.struct; +/** Exception thrown when encountering a bad schema. */ public class BadSchemaException extends Exception { + /** The bad schema field. */ private final String m_field; - public BadSchemaException(String s) { - super(s); + /** + * Constructs a BadSchemaException. + * + * @param message the detail message. + */ + public BadSchemaException(String message) { + super(message); m_field = ""; } + /** + * Constructs a BadSchemaException. + * + * @param message the detail message. + * @param cause The cause (which is saved for later retrieval by the {@link #getCause()} method). + */ public BadSchemaException(String message, Throwable cause) { super(message, cause); m_field = ""; } + /** + * Constructs a BadSchemaException. + * + * @param cause The cause (which is saved for later retrieval by the {@link #getCause()} method). + */ public BadSchemaException(Throwable cause) { super(cause); m_field = ""; } - public BadSchemaException(String field, String s) { - super(s); + /** + * Constructs a BadSchemaException. + * + * @param field The bad schema field. + * @param message the detail message. + */ + public BadSchemaException(String field, String message) { + super(message); m_field = field; } + /** + * Constructs a BadSchemaException. + * + * @param field The bad schema field. + * @param message the detail message. + * @param cause The cause (which is saved for later retrieval by the {@link #getCause()} method). + */ public BadSchemaException(String field, String message, Throwable cause) { super(message, cause); m_field = field; } + /** + * Gets the name of the bad schema field. + * + * @return The name of the bad schema field, or an empty string if not applicable. + */ public String getField() { return m_field; } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/DynamicStruct.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/DynamicStruct.java index 1f9fdc379a8..de8350e8d29 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/struct/DynamicStruct.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/DynamicStruct.java @@ -378,6 +378,7 @@ public void setDoubleField(StructFieldDescriptor field, double value) { * @throws IllegalArgumentException if field is not a member of this struct * @throws IllegalStateException if struct descriptor is invalid */ + @SuppressWarnings({"PMD.CollapsibleIfStatements", "PMD.AvoidDeeplyNestedIfStmts"}) public String getStringField(StructFieldDescriptor field) { if (field.getType() != StructFieldType.kChar) { throw new UnsupportedOperationException("field is not char type"); @@ -390,7 +391,56 @@ public String getStringField(StructFieldDescriptor field) { } byte[] bytes = new byte[field.m_arraySize]; m_data.position(field.m_offset).get(bytes, 0, field.m_arraySize); - return new String(bytes, StandardCharsets.UTF_8); + // Find last non zero character + int stringLength = bytes.length; + for (; stringLength > 0; stringLength--) { + if (bytes[stringLength - 1] != 0) { + break; + } + } + // If string is all zeroes, its empty and return an empty string. + if (stringLength == 0) { + return ""; + } + // Check if the end of the string is in the middle of a continuation byte or + // not. + if ((bytes[stringLength - 1] & 0x80) != 0) { + // This is a UTF8 continuation byte. Make sure its valid. + // Walk back until initial byte is found + int utf8StartByte = stringLength; + for (; utf8StartByte > 0; utf8StartByte--) { + if ((bytes[utf8StartByte - 1] & 0x40) != 0) { + // Having 2nd bit set means start byte + break; + } + } + if (utf8StartByte == 0) { + // This case means string only contains continuation bytes + return ""; + } + utf8StartByte--; + // Check if its a 2, 3, or 4 byte + byte checkByte = bytes[utf8StartByte]; + if ((checkByte & 0xE0) == 0xC0) { + // 2 byte, need 1 more byte + if (utf8StartByte != stringLength - 2) { + stringLength = utf8StartByte; + } + } else if ((checkByte & 0xF0) == 0xE0) { + // 3 byte, need 2 more bytes + if (utf8StartByte != stringLength - 3) { + stringLength = utf8StartByte; + } + } else if ((checkByte & 0xF8) == 0xF0) { + // 4 byte, need 3 more bytes + if (utf8StartByte != stringLength - 4) { + stringLength = utf8StartByte; + } + } + // If we get here, the string is either completely garbage or fine. + } + + return new String(bytes, 0, stringLength, StandardCharsets.UTF_8); } /** @@ -398,11 +448,12 @@ public String getStringField(StructFieldDescriptor field) { * * @param field field descriptor * @param value field value + * @return true if the full value fit in the struct, false if truncated * @throws UnsupportedOperationException if field is not char type * @throws IllegalArgumentException if field is not a member of this struct * @throws IllegalStateException if struct descriptor is invalid */ - public void setStringField(StructFieldDescriptor field, String value) { + public boolean setStringField(StructFieldDescriptor field, String value) { if (field.getType() != StructFieldType.kChar) { throw new UnsupportedOperationException("field is not char type"); } @@ -414,10 +465,12 @@ public void setStringField(StructFieldDescriptor field, String value) { } ByteBuffer bb = StandardCharsets.UTF_8.encode(value); int len = Math.min(bb.remaining(), field.m_arraySize); + boolean copiedFull = len == bb.remaining(); m_data.position(field.m_offset).put(bb.limit(len)); for (int i = len; i < field.m_arraySize; i++) { m_data.put((byte) 0); } + return copiedFull; } /** diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructBuffer.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructBuffer.java index 8bd5768f09e..a5d04e09685 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructBuffer.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructBuffer.java @@ -12,7 +12,7 @@ /** * Reusable buffer for serialization/deserialization to/from a raw struct. * - * @param object type + * @param Object type. */ public final class StructBuffer { private StructBuffer(Struct struct) { @@ -21,6 +21,13 @@ private StructBuffer(Struct struct) { m_struct = struct; } + /** + * Returns a StructBuffer for the given struct. + * + * @param struct A struct. + * @param Object type. + * @return A StructBuffer for the given struct. + */ public static StructBuffer create(Struct struct) { return new StructBuffer<>(struct); } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptorDatabase.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptorDatabase.java index 1eb58c9baf0..459fa9e10f8 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptorDatabase.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptorDatabase.java @@ -14,6 +14,9 @@ /** Database of raw struct dynamic descriptors. */ public class StructDescriptorDatabase { + /** Default constructor. */ + public StructDescriptorDatabase() {} + /** * Adds a structure schema to the database. If the struct references other structs that have not * yet been added, it will not be valid until those structs are also added. diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldDescriptor.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldDescriptor.java index 25a69a575bc..1d5db379fdb 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldDescriptor.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldDescriptor.java @@ -206,7 +206,7 @@ public long getUintMax() { * @return minimum value */ public long getIntMin() { - return (-(m_bitMask >> 1)) - 1; + return -(m_bitMask >> 1) - 1; } /** diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldType.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldType.java index 28d5d8ec31b..4b3cf179d3d 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldType.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldType.java @@ -6,29 +6,46 @@ /** Known data types for raw struct dynamic fields (see StructFieldDescriptor). */ public enum StructFieldType { + /** bool. */ kBool("bool", false, false, 1), + /** char. */ kChar("char", false, false, 1), + /** int8. */ kInt8("int8", true, false, 1), + /** int16. */ kInt16("int16", true, false, 2), + /** int32. */ kInt32("int32", true, false, 4), + /** int64. */ kInt64("int64", true, false, 8), + /** uint8. */ kUint8("uint8", false, true, 1), + /** uint16. */ kUint16("uint16", false, true, 2), + /** uint32. */ kUint32("uint32", false, true, 4), + /** uint64. */ kUint64("uint64", false, true, 8), + /** float. */ kFloat("float", false, false, 4), + /** double. */ kDouble("double", false, false, 8), + /** struct. */ kStruct("struct", false, false, 0); + /** The name of the data type. */ @SuppressWarnings("MemberName") public final String name; + /** Indicates if the data type is a signed integer. */ @SuppressWarnings("MemberName") public final boolean isInt; + /** Indicates if the data type is an unsigned integer. */ @SuppressWarnings("MemberName") public final boolean isUint; + /** The size (in bytes) of the data type. */ @SuppressWarnings("MemberName") public final int size; diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructSerializable.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructSerializable.java index 93cf6f262b0..a8d1fdd8078 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructSerializable.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructSerializable.java @@ -12,4 +12,4 @@ *

While this cannot be enforced by the interface, any class implementing this interface should * provide a public final static `struct` member variable. */ -public interface StructSerializable extends WPISerializable {} +public interface StructSerializable extends WPISerializable {} diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParseException.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParseException.java index 9fa843ca70e..5e71c79ca95 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParseException.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParseException.java @@ -4,24 +4,50 @@ package edu.wpi.first.util.struct.parser; +/** Exception for parsing errors. */ public class ParseException extends Exception { + /** The parser position. */ private final int m_pos; + /** + * Constructs a ParseException. + * + * @param pos The parser position. + * @param s Reason for parse failure. + */ public ParseException(int pos, String s) { super(s); m_pos = pos; } + /** + * Constructs a ParseException. + * + * @param pos The parser position. + * @param message Reason for parse failure. + * @param cause Exception that caused the parser failure. + */ public ParseException(int pos, String message, Throwable cause) { super(message, cause); m_pos = pos; } + /** + * Constructs a ParseException. + * + * @param pos The parser position. + * @param cause Exception that caused the parser failure. + */ public ParseException(int pos, Throwable cause) { super(cause); m_pos = pos; } + /** + * Returns position in parsed string. + * + * @return Position in parsed string. + */ public int getPosition() { return m_pos; } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedDeclaration.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedDeclaration.java index 8184ae5e56f..29743ab05a6 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedDeclaration.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedDeclaration.java @@ -8,18 +8,26 @@ /** Raw struct schema declaration. */ public class ParsedDeclaration { + /** Type string. */ @SuppressWarnings("MemberName") public String typeString; + /** Name. */ @SuppressWarnings("MemberName") public String name; + /** Enum values. */ @SuppressWarnings("MemberName") public Map enumValues; + /** Array size. */ @SuppressWarnings("MemberName") public int arraySize = 1; + /** Bit width. */ @SuppressWarnings("MemberName") public int bitWidth; + + /** Default constructor. */ + public ParsedDeclaration() {} } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedSchema.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedSchema.java index 2ca175314c2..2f7312af9a8 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedSchema.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedSchema.java @@ -9,6 +9,10 @@ /** Raw struct schema. */ public class ParsedSchema { + /** Declarations. */ @SuppressWarnings("MemberName") public List declarations = new ArrayList<>(); + + /** Default constructor. */ + public ParsedSchema() {} } diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/TokenKind.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/TokenKind.java index 85afa4a3bf1..fb74c6f8118 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/TokenKind.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/TokenKind.java @@ -6,17 +6,40 @@ /** A lexed raw struct schema token. */ public enum TokenKind { + /** Unknown. */ kUnknown("unknown"), + + /** Integer. */ kInteger("integer"), + + /** Identifier. */ kIdentifier("identifier"), + + /** Left square bracket. */ kLeftBracket("'['"), + + /** Right square bracket. */ kRightBracket("']'"), + + /** Left curly brace. */ kLeftBrace("'{'"), + + /** Right curly brace. */ kRightBrace("'}'"), + + /** Colon. */ kColon("':'"), + + /** Semicolon. */ kSemicolon("';'"), + + /** Comma. */ kComma("','"), + + /** Equals. */ kEquals("'='"), + + /** End of input. */ kEndOfInput(""); private final String m_name; diff --git a/wpiutil/src/main/native/cpp/DataLog.cpp b/wpiutil/src/main/native/cpp/DataLog.cpp index 40dab2a1b59..bbde5e7b7c7 100644 --- a/wpiutil/src/main/native/cpp/DataLog.cpp +++ b/wpiutil/src/main/native/cpp/DataLog.cpp @@ -1205,4 +1205,17 @@ void WPI_DataLog_AppendStringArray(struct WPI_DataLog* datalog, int entry, timestamp); } +void WPI_DataLog_AddSchemaString(struct WPI_DataLog* datalog, const char* name, + const char* type, const char* schema, + int64_t timestamp) { + reinterpret_cast(datalog)->AddSchema(name, type, schema, timestamp); +} + +void WPI_DataLog_AddSchema(struct WPI_DataLog* datalog, const char* name, + const char* type, const uint8_t* schema, + size_t schema_len, int64_t timestamp) { + reinterpret_cast(datalog)->AddSchema( + name, type, std::span{schema, schema_len}, timestamp); +} + } // extern "C" diff --git a/wpiutil/src/main/native/cpp/struct/DynamicStruct.cpp b/wpiutil/src/main/native/cpp/struct/DynamicStruct.cpp index 7ae9271ae04..8d43f537444 100644 --- a/wpiutil/src/main/native/cpp/struct/DynamicStruct.cpp +++ b/wpiutil/src/main/native/cpp/struct/DynamicStruct.cpp @@ -349,16 +349,75 @@ void MutableDynamicStruct::SetData(std::span data) { std::copy(data.begin(), data.begin() + m_desc->GetSize(), m_data.begin()); } -void MutableDynamicStruct::SetStringField(const StructFieldDescriptor* field, +std::string_view DynamicStruct::GetStringField( + const StructFieldDescriptor* field) const { + assert(field->m_type == StructFieldType::kChar); + assert(field->m_parent == m_desc); + assert(m_desc->IsValid()); + // Find last non zero character + size_t stringLength; + for (stringLength = field->m_arraySize; stringLength > 0; stringLength--) { + if (m_data[field->m_offset + stringLength - 1] != 0) { + break; + } + } + // If string is all zeroes, its empty and return an empty string. + if (stringLength == 0) { + return ""; + } + // Check if the end of the string is in the middle of a continuation byte or + // not. + if ((m_data[field->m_offset + stringLength - 1] & 0x80) != 0) { + // This is a UTF8 continuation byte. Make sure its valid. + // Walk back until initial byte is found + size_t utf8StartByte = stringLength; + for (; utf8StartByte > 0; utf8StartByte--) { + if ((m_data[field->m_offset + utf8StartByte - 1] & 0x40) != 0) { + // Having 2nd bit set means start byte + break; + } + } + if (utf8StartByte == 0) { + // This case means string only contains continuation bytes + return ""; + } + utf8StartByte--; + // Check if its a 2, 3, or 4 byte + uint8_t checkByte = m_data[field->m_offset + utf8StartByte]; + if ((checkByte & 0xE0) == 0xC0) { + // 2 byte, need 1 more byte + if (utf8StartByte != stringLength - 2) { + stringLength = utf8StartByte; + } + } else if ((checkByte & 0xF0) == 0xE0) { + // 3 byte, need 2 more bytes + if (utf8StartByte != stringLength - 3) { + stringLength = utf8StartByte; + } + } else if ((checkByte & 0xF8) == 0xF0) { + // 4 byte, need 3 more bytes + if (utf8StartByte != stringLength - 4) { + stringLength = utf8StartByte; + } + } + // If we get here, the string is either completely garbage or fine. + } + return {reinterpret_cast(&m_data[field->m_offset]), + stringLength}; +} + +bool MutableDynamicStruct::SetStringField(const StructFieldDescriptor* field, std::string_view value) { assert(field->m_type == StructFieldType::kChar); assert(field->m_parent == m_desc); assert(m_desc->IsValid()); size_t len = (std::min)(field->m_arraySize, value.size()); + bool copiedFull = len == value.size(); std::copy(value.begin(), value.begin() + len, reinterpret_cast(&m_data[field->m_offset])); - std::fill(&m_data[field->m_offset + len], - &m_data[field->m_offset + field->m_arraySize], 0); + auto toFill = m_data.subspan(field->m_offset + len, field->m_arraySize - len); + std::fill(toFill.begin(), toFill.end(), 0); + return copiedFull; } void MutableDynamicStruct::SetStructField(const StructFieldDescriptor* field, diff --git a/wpiutil/src/main/native/include/wpi/DataLog.h b/wpiutil/src/main/native/include/wpi/DataLog.h index 2f79a3a17b1..6187662c00d 100644 --- a/wpiutil/src/main/native/include/wpi/DataLog.h +++ b/wpiutil/src/main/native/include/wpi/DataLog.h @@ -1373,6 +1373,14 @@ void WPI_DataLog_AppendStringArray(struct WPI_DataLog* datalog, int entry, const WPI_DataLog_String* arr, size_t len, int64_t timestamp); +void WPI_DataLog_AddSchemaString(struct WPI_DataLog* datalog, const char* name, + const char* type, const char* schema, + int64_t timestamp); + +void WPI_DataLog_AddSchema(struct WPI_DataLog* datalog, const char* name, + const char* type, const uint8_t* schema, + size_t schema_len, int64_t timestamp); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/wpiutil/src/main/native/include/wpi/DataLogReader.h b/wpiutil/src/main/native/include/wpi/DataLogReader.h index b1153e4228d..cb9a8cb8ffb 100644 --- a/wpiutil/src/main/native/include/wpi/DataLogReader.h +++ b/wpiutil/src/main/native/include/wpi/DataLogReader.h @@ -288,7 +288,7 @@ class DataLogIterator { pointer operator->() const { return &this->operator*(); } - private: + protected: const DataLogReader* m_reader; size_t m_pos; mutable bool m_valid = false; diff --git a/wpiutil/src/main/native/include/wpi/circular_buffer.h b/wpiutil/src/main/native/include/wpi/circular_buffer.h index f0a792b956c..a40a627db41 100644 --- a/wpiutil/src/main/native/include/wpi/circular_buffer.h +++ b/wpiutil/src/main/native/include/wpi/circular_buffer.h @@ -13,10 +13,17 @@ namespace wpi { /** * This is a simple circular buffer so we don't need to "bucket brigade" copy * old values. + * + * @tparam T Buffer element type. */ template class circular_buffer { public: + /** + * Constructs a circular buffer. + * + * @param size Maximum number of buffer elements. + */ explicit circular_buffer(size_t size) : m_data(size, T{}) {} circular_buffer(const circular_buffer&) = default; diff --git a/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h b/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h index 4115420c79f..1913d7cc72f 100644 --- a/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h +++ b/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h @@ -15,12 +15,20 @@ namespace wpi { +/** + * Helper class for building Sendable dashboard representations. + */ class SendableBuilder { public: /** * The backend kinds used for the sendable builder. */ - enum BackendKind { kUnknown, kNetworkTables }; + enum BackendKind { + /// Unknown. + kUnknown, + /// NetworkTables. + kNetworkTables + }; virtual ~SendableBuilder() = default; diff --git a/wpiutil/src/main/native/include/wpi/static_circular_buffer.h b/wpiutil/src/main/native/include/wpi/static_circular_buffer.h index 09446dfcb5e..0d38f25ebea 100644 --- a/wpiutil/src/main/native/include/wpi/static_circular_buffer.h +++ b/wpiutil/src/main/native/include/wpi/static_circular_buffer.h @@ -13,6 +13,9 @@ namespace wpi { /** * This is a simple circular buffer so we don't need to "bucket brigade" copy * old values. + * + * @tparam T Buffer element type. + * @tparam N Maximum number of buffer elements. */ template class static_circular_buffer { diff --git a/wpiutil/src/main/native/include/wpi/struct/DynamicStruct.h b/wpiutil/src/main/native/include/wpi/struct/DynamicStruct.h index b88894c09d0..f49db2514ec 100644 --- a/wpiutil/src/main/native/include/wpi/struct/DynamicStruct.h +++ b/wpiutil/src/main/native/include/wpi/struct/DynamicStruct.h @@ -32,18 +32,31 @@ class StructDescriptorDatabase; * Known data types for raw struct dynamic fields (see StructFieldDescriptor). */ enum class StructFieldType { + /// bool. kBool, + /// char. kChar, + /// int8. kInt8, + /// int16. kInt16, + /// int32. kInt32, + /// int64. kInt64, + /// uint8. kUint8, + /// uint16. kUint16, + /// uint32. kUint32, + /// uint64. kUint64, + /// float. kFloat, + /// double. kDouble, + /// struct. kStruct }; @@ -459,13 +472,7 @@ class DynamicStruct { * @param field field descriptor * @return field value */ - std::string_view GetStringField(const StructFieldDescriptor* field) const { - assert(field->m_type == StructFieldType::kChar); - assert(field->m_parent == m_desc); - assert(m_desc->IsValid()); - return {reinterpret_cast(&m_data[field->m_offset]), - field->m_arraySize}; - } + std::string_view GetStringField(const StructFieldDescriptor* field) const; /** * Gets the value of a struct field. @@ -597,8 +604,9 @@ class MutableDynamicStruct : public DynamicStruct { * * @param field field descriptor * @param value field value + * @return true if the full value fit in the struct, false if truncated */ - void SetStringField(const StructFieldDescriptor* field, + bool SetStringField(const StructFieldDescriptor* field, std::string_view value); /** diff --git a/wpiutil/src/main/native/include/wpi/struct/SchemaParser.h b/wpiutil/src/main/native/include/wpi/struct/SchemaParser.h index a8cf1a403f4..4e5a1df4570 100644 --- a/wpiutil/src/main/native/include/wpi/struct/SchemaParser.h +++ b/wpiutil/src/main/native/include/wpi/struct/SchemaParser.h @@ -17,18 +17,31 @@ namespace wpi::structparser { * A lexed raw struct schema token. */ struct Token { + /** A lexed raw struct schema token kind. */ enum Kind { + /// Unknown. kUnknown, + /// Integer. kInteger, + /// Identifier. kIdentifier, + /// Left square bracket. kLeftBracket, + /// Right square bracket. kRightBracket, + /// Left curly brace. kLeftBrace, + /// Right curly brace. kRightBrace, + /// Colon. kColon, + /// Semicolon. kSemicolon, + /// Comma. kComma, + /// Equals. kEquals, + /// End of input. kEndOfInput, }; diff --git a/wpiutil/src/main/native/thirdparty/expected/include/wpi/expected b/wpiutil/src/main/native/thirdparty/expected/include/wpi/expected new file mode 100644 index 00000000000..67420e6b0df --- /dev/null +++ b/wpiutil/src/main/native/thirdparty/expected/include/wpi/expected @@ -0,0 +1,2444 @@ +/// +// expected - An implementation of std::expected with extensions +// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) +// +// Documentation available at http://tl.tartanllama.xyz/ +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to the +// public domain worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. If not, see +// . +/// + +#ifndef WPI_EXPECTED_HPP +#define WPI_EXPECTED_HPP + +#define WPI_EXPECTED_VERSION_MAJOR 1 +#define WPI_EXPECTED_VERSION_MINOR 1 +#define WPI_EXPECTED_VERSION_PATCH 0 + +#include +#include +#include +#include + +#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#define WPI_EXPECTED_EXCEPTIONS_ENABLED +#endif + +#if (defined(_MSC_VER) && _MSC_VER == 1900) +#define WPI_EXPECTED_MSVC2015 +#define WPI_EXPECTED_MSVC2015_CONSTEXPR +#else +#define WPI_EXPECTED_MSVC2015_CONSTEXPR constexpr +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +#define WPI_EXPECTED_GCC49 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ + !defined(__clang__)) +#define WPI_EXPECTED_GCC54 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ + !defined(__clang__)) +#define WPI_EXPECTED_GCC55 +#endif + +#if !defined(WPI_ASSERT) +//can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug +#if (__cplusplus > 201103L) && !defined(WPI_EXPECTED_GCC49) +#include +#define WPI_ASSERT(x) assert(x) +#else +#define WPI_ASSERT(x) +#endif +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions + +#define WPI_EXPECTED_NO_CONSTRR +// GCC < 5 doesn't support some standard C++11 type traits +#define WPI_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor +#define WPI_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign + +// This one will be different for GCC 5.7 if it's ever supported +#define WPI_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible + +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks +// std::vector for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) +#ifndef WPI_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#define WPI_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +namespace wpi { +namespace detail { +template +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; +#ifdef _GLIBCXX_VECTOR +template +struct is_trivially_copy_constructible> : std::false_type {}; +#endif +} // namespace detail +} // namespace wpi +#endif + +#define WPI_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + wpi::detail::is_trivially_copy_constructible +#define WPI_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define WPI_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#else +#define WPI_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible +#define WPI_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define WPI_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#endif + +#if __cplusplus > 201103L +#define WPI_EXPECTED_CXX14 +#endif + +#ifdef WPI_EXPECTED_GCC49 +#define WPI_EXPECTED_GCC49_CONSTEXPR +#else +#define WPI_EXPECTED_GCC49_CONSTEXPR constexpr +#endif + +#if (__cplusplus == 201103L || defined(WPI_EXPECTED_MSVC2015) || \ + defined(WPI_EXPECTED_GCC49)) +#define WPI_EXPECTED_11_CONSTEXPR +#else +#define WPI_EXPECTED_11_CONSTEXPR constexpr +#endif + +namespace wpi { +template class expected; + +#ifndef WPI_MONOSTATE_INPLACE_MUTEX +#define WPI_MONOSTATE_INPLACE_MUTEX +class monostate {}; + +struct in_place_t { + explicit in_place_t() = default; +}; +static constexpr in_place_t in_place{}; +#endif + +template class unexpected { +public: + static_assert(!std::is_same::value, "E must not be void"); + + unexpected() = delete; + constexpr explicit unexpected(const E &e) : m_val(e) {} + + constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} + + template ::value>::type * = nullptr> + constexpr explicit unexpected(Args &&...args) + : m_val(std::forward(args)...) {} + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(std::initializer_list l, Args &&...args) + : m_val(l, std::forward(args)...) {} + + constexpr const E &value() const & { return m_val; } + WPI_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } + WPI_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } + constexpr const E &&value() const && { return std::move(m_val); } + +private: + E m_val; +}; + +#ifdef __cpp_deduction_guides +template unexpected(E) -> unexpected; +#endif + +template +constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() == rhs.value(); +} +template +constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() != rhs.value(); +} +template +constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() < rhs.value(); +} +template +constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() <= rhs.value(); +} +template +constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() > rhs.value(); +} +template +constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() >= rhs.value(); +} + +template +unexpected::type> make_unexpected(E &&e) { + return unexpected::type>(std::forward(e)); +} + +struct unexpect_t { + unexpect_t() = default; +}; +static constexpr unexpect_t unexpect{}; + +namespace detail { +template +[[noreturn]] WPI_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef WPI_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward(e); +#else + (void)e; +#ifdef _MSC_VER + __assume(0); +#else + __builtin_unreachable(); +#endif +#endif +} + +#ifndef WPI_TRAITS_MUTEX +#define WPI_TRAITS_MUTEX +// C++14-style aliases for brevity +template using remove_const_t = typename std::remove_const::type; +template +using remove_reference_t = typename std::remove_reference::type; +template using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; + +// std::conjunction from C++17 +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, B>::type {}; + +#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +#define WPI_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#endif + +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +#ifdef WPI_TRAITS_LIBCXX_MEM_FN_WORKAROUND +template +struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; + +template struct is_const_or_const_ref : std::false_type {}; +template struct is_const_or_const_ref : std::true_type {}; +template struct is_const_or_const_ref : std::true_type {}; +#endif + +// std::invoke from C++17 +// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround +template < + typename Fn, typename... Args, +#ifdef WPI_TRAITS_LIBCXX_MEM_FN_WORKAROUND + typename = enable_if_t::value && + is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, int = 0> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} + +template >::value>> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} + +// std::invoke_result from C++17 +template struct invoke_result_impl; + +template +struct invoke_result_impl< + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result = invoke_result_impl; + +template +using invoke_result_t = typename invoke_result::type; + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template struct is_swappable : std::true_type {}; + +template struct is_nothrow_swappable : std::true_type {}; +#else +// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept +namespace swap_adl_tests { +// if swap ADL finds this then it would call std::swap otherwise (same +// signature) +struct tag {}; + +template tag swap(T &, T &); +template tag swap(T (&a)[N], T (&b)[N]); + +// helper functions to test if an unqualified swap is possible, and if it +// becomes std::swap +template std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); + +template std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); + +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; + +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; + +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || + is_swappable::value)> {}; + +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; +#endif +#endif + +// Trait for checking if a type is a wpi::expected +template struct is_expected_impl : std::false_type {}; +template +struct is_expected_impl> : std::true_type {}; +template using is_expected = is_expected_impl>; + +template +using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value && + !std::is_same, detail::decay_t>::value>; + +template +using expected_enable_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; + +template +using is_void_or = conditional_t::value, std::true_type, U>; + +template +using is_copy_constructible_or_void = + is_void_or>; + +template +using is_move_constructible_or_void = + is_void_or>; + +template +using is_copy_assignable_or_void = is_void_or>; + +template +using is_move_assignable_or_void = is_void_or>; + +} // namespace detail + +namespace detail { +struct no_init_t {}; +static constexpr no_init_t no_init{}; + +// Implements the storage of the values, and ensures that the destructor is +// trivial if it can be. +// +// This specialization is for where neither `T` or `E` is trivially +// destructible, so the destructors must be called on destruction of the +// `expected` +template ::value, + bool = std::is_trivially_destructible::value> +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } else { + m_unexpect.~unexpected(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// This specialization is for when both `T` and `E` are trivially-destructible, +// so the destructor of the `expected` can be trivial. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// T is trivial, E is not. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + WPI_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) + : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// E is trivial, T is not. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is trivially-destructible +template struct expected_storage_base { + #if __GNUC__ <= 5 + //no constexpr for GCC 4/5 bug + #else + WPI_EXPECTED_MSVC2015_CONSTEXPR + #endif + expected_storage_base() : m_has_val(true) {} + + constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + struct dummy {}; + union { + unexpected m_unexpect; + dummy m_val; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is not trivially-destructible +template struct expected_storage_base { + constexpr expected_storage_base() : m_dummy(), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + unexpected m_unexpect; + char m_dummy; + }; + bool m_has_val; +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct(Args &&...args) noexcept { + new (std::addressof(this->m_val)) T(std::forward(args)...); + this->m_has_val = true; + } + + template void construct_with(Rhs &&rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward(rhs).get()); + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + +#ifdef WPI_EXPECTED_EXCEPTIONS_ENABLED + + // These assign overloads ensure that the most efficient assignment + // implementation is used while maintaining the strong exception guarantee. + // The problematic case is where rhs has a value, but *this does not. + // + // This overload handles the case where we can just copy-construct `T` + // directly into place without throwing. + template ::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + // This overload handles the case where we can attempt to create a copy of + // `T`, then no-throw move it into place if the copy was successful. + template ::value && + std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + T tmp = rhs.get(); + geterr().~unexpected(); + construct(std::move(tmp)); + } else { + assign_common(rhs); + } + } + + // This overload is the worst-case, where we have to move-construct the + // unexpected value into temporary storage, then try to copy the T into place. + // If the construction succeeds, then everything is fine, but if it throws, + // then we move the old unexpected value back into place before rethrowing the + // exception. + template ::value && + !std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); + +#ifdef WPI_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(rhs.get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(rhs.get()); +#endif + } else { + assign_common(rhs); + } + } + + // These overloads do the same as above, but for rvalues + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); +#ifdef WPI_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(std::move(rhs).get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(std::move(rhs).get()); +#endif + } else { + assign_common(std::move(rhs)); + } + } + +#else + + // If exceptions are disabled then we can just copy-construct + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + +#endif + + // The common part of move/copy assigning + template void assign_common(Rhs &&rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward(rhs).geterr(); + } + } + } + + bool has_value() const { return this->m_has_val; } + + WPI_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } + constexpr const T &get() const & { return this->m_val; } + WPI_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } +#ifndef WPI_EXPECTED_NO_CONSTRR + constexpr const T &&get() const && { return std::move(this->m_val); } +#endif + + WPI_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + WPI_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef WPI_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + WPI_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct() noexcept { this->m_has_val = true; } + + // This function doesn't use its argument, but needs it so that code in + // levels above this can work independently of whether T is void + template void construct_with(Rhs &&) noexcept { + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + + template void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected(); + construct(); + } else { + geterr() = std::forward(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward(rhs).geterr()); + } + } + } + + bool has_value() const { return this->m_has_val; } + + WPI_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + WPI_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef WPI_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + WPI_EXPECTED_11_CONSTEXPR void destroy_val() { + // no-op + } +}; + +// This class manages conditionally having a trivial copy constructor +// This specialization is for when T and E are trivially copy constructible +template :: + value &&WPI_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; +}; + +// This specialization is for when T or E are not trivially copy constructible +template +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; + + expected_copy_base() = default; + expected_copy_base(const expected_copy_base &rhs) + : expected_operations_base(no_init) { + if (rhs.has_value()) { + this->construct_with(rhs); + } else { + this->construct_error(rhs.geterr()); + } + } + + expected_copy_base(expected_copy_base &&rhs) = default; + expected_copy_base &operator=(const expected_copy_base &rhs) = default; + expected_copy_base &operator=(expected_copy_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move constructor +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_constructible. We +// have to make do with a non-trivial move constructor even if T is trivially +// move constructible +#ifndef WPI_EXPECTED_GCC49 +template >::value + &&std::is_trivially_move_constructible::value> +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; +}; +#else +template struct expected_move_base; +#endif +template +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; + + expected_move_base() = default; + expected_move_base(const expected_move_base &rhs) = default; + + expected_move_base(expected_move_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value) + : expected_copy_base(no_init) { + if (rhs.has_value()) { + this->construct_with(std::move(rhs)); + } else { + this->construct_error(std::move(rhs.geterr())); + } + } + expected_move_base &operator=(const expected_move_base &rhs) = default; + expected_move_base &operator=(expected_move_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial copy assignment operator +template >::value + &&WPI_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value + &&WPI_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value + &&WPI_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value> +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; +}; + +template +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; + + expected_copy_assign_base() = default; + expected_copy_assign_base(const expected_copy_assign_base &rhs) = default; + + expected_copy_assign_base(expected_copy_assign_base &&rhs) = default; + expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) { + this->assign(rhs); + return *this; + } + expected_copy_assign_base & + operator=(expected_copy_assign_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move assignment operator +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_assignable. We have +// to make do with a non-trivial move assignment operator even if T is trivially +// move assignable +#ifndef WPI_EXPECTED_GCC49 +template , + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>:: + value &&std::is_trivially_destructible::value + &&std::is_trivially_move_constructible::value + &&std::is_trivially_move_assignable::value> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; +}; +#else +template struct expected_move_assign_base; +#endif + +template +struct expected_move_assign_base + : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; + + expected_move_assign_base() = default; + expected_move_assign_base(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base(expected_move_assign_base &&rhs) = default; + + expected_move_assign_base & + operator=(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base & + operator=(expected_move_assign_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value + &&std::is_nothrow_move_assignable::value) { + this->assign(std::move(rhs)); + return *this; + } +}; + +// expected_delete_ctor_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible +template ::value && + std::is_copy_constructible::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value)> +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +// expected_delete_assign_base will conditionally delete copy and move +// constructors depending on whether T and E are copy/move constructible + +// assignable +template ::value && + std::is_copy_constructible::value && + is_copy_assignable_or_void::value && + std::is_copy_assignable::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value && + is_move_assignable_or_void::value && + std::is_move_assignable::value)> +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +// This is needed to be able to construct the expected_default_ctor_base which +// follows, while still conditionally deleting the default constructor. +struct default_constructor_tag { + explicit constexpr default_constructor_tag() = default; +}; + +// expected_default_ctor_base will ensure that expected has a deleted default +// consturctor if T is not default constructible. +// This specialization is for when T is default constructible +template ::value || std::is_void::value> +struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = default; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; + +// This specialization is for when T is not default constructible +template struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = delete; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; +} // namespace detail + +template class bad_expected_access : public std::exception { +public: + explicit bad_expected_access(E e) : m_val(std::move(e)) {} + + virtual const char *what() const noexcept override { + return "Bad expected access"; + } + + const E &error() const & { return m_val; } + E &error() & { return m_val; } + const E &&error() const && { return std::move(m_val); } + E &&error() && { return std::move(m_val); } + +private: + E m_val; +}; + +/// An `expected` object is an object that contains the storage for +/// another object and manages the lifetime of this contained object `T`. +/// Alternatively it could contain the storage for another unexpected object +/// `E`. The contained object may not be initialized after the expected object +/// has been initialized, and may not be destroyed before the expected object +/// has been destroyed. The initialization state of the contained object is +/// tracked by the expected object. +template +class expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_ctor_base { + static_assert(!std::is_reference::value, "T must not be a reference"); + static_assert(!std::is_same::type>::value, + "T must not be in_place_t"); + static_assert(!std::is_same::type>::value, + "T must not be unexpect_t"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); + static_assert(!std::is_reference::value, "E must not be a reference"); + + T *valptr() { return std::addressof(this->m_val); } + const T *valptr() const { return std::addressof(this->m_val); } + unexpected *errptr() { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { + return std::addressof(this->m_unexpect); + } + + template ::value> * = nullptr> + WPI_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } + WPI_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } + + template ::value> * = nullptr> + constexpr const U &val() const { + return this->m_val; + } + constexpr const unexpected &err() const { return this->m_unexpect; } + + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; + +public: + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; + +#if defined(WPI_EXPECTED_CXX14) && !defined(WPI_EXPECTED_GCC49) && \ + !defined(WPI_EXPECTED_GCC54) && !defined(WPI_EXPECTED_GCC55) + template WPI_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward(f)); + } + template WPI_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward(f)); + } + template constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef WPI_EXPECTED_NO_CONSTRR + template constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif + +#else + template + WPI_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) & -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + template + WPI_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) && -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef WPI_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(WPI_EXPECTED_CXX14) && !defined(WPI_EXPECTED_GCC49) && \ + !defined(WPI_EXPECTED_GCC54) && !defined(WPI_EXPECTED_GCC55) + template WPI_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template WPI_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + WPI_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + WPI_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef WPI_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(WPI_EXPECTED_CXX14) && !defined(WPI_EXPECTED_GCC49) && \ + !defined(WPI_EXPECTED_GCC54) && !defined(WPI_EXPECTED_GCC55) + template WPI_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template WPI_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + WPI_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + WPI_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef WPI_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(WPI_EXPECTED_CXX14) && !defined(WPI_EXPECTED_GCC49) && \ + !defined(WPI_EXPECTED_GCC54) && !defined(WPI_EXPECTED_GCC55) + template WPI_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template WPI_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + WPI_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + WPI_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef WPI_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif +#if defined(WPI_EXPECTED_CXX14) && !defined(WPI_EXPECTED_GCC49) && \ + !defined(WPI_EXPECTED_GCC54) && !defined(WPI_EXPECTED_GCC55) + template WPI_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template WPI_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + WPI_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + WPI_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef WPI_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + template expected WPI_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward(f)); + } + + template expected WPI_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward(f)); + } + + template expected constexpr or_else(F &&f) const & { + return or_else_impl(*this, std::forward(f)); + } + +#ifndef WPI_EXPECTED_NO_CONSTRR + template expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward(f)); + } +#endif + constexpr expected() = default; + constexpr expected(const expected &rhs) = default; + constexpr expected(expected &&rhs) = default; + expected &operator=(const expected &rhs) = default; + expected &operator=(expected &&rhs) = default; + + template ::value> * = + nullptr> + constexpr expected(in_place_t, Args &&...args) + : impl_base(in_place, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) + : impl_base(in_place, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr> + constexpr explicit expected(unexpect_t, Args &&...args) + : impl_base(unexpect, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected(unexpect_t, std::initializer_list il, + Args &&...args) + : impl_base(unexpect, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + explicit WPI_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + WPI_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template < + class U, class G, + detail::enable_if_t::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit WPI_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U, class G, + detail::enable_if_t<(std::is_convertible::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + WPI_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit WPI_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + WPI_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + err().~unexpected(); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } + + return *this; + } + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef WPI_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; +#endif + } + + return *this; + } + + template ::value && + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; + } else { + this->destroy_val(); + ::new (errptr()) unexpected(rhs); + this->m_has_val = false; + } + + return *this; + } + + template ::value && + std::is_move_assignable::value> * = nullptr> + expected &operator=(unexpected &&rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + this->destroy_val(); + ::new (errptr()) unexpected(std::move(rhs)); + this->m_has_val = false; + } + + return *this; + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + } else { + err().~unexpected(); + this->m_has_val = true; + } + ::new (valptr()) T(std::forward(args)...); + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + ::new (valptr()) T(std::forward(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef WPI_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; +#endif + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + err().~unexpected(); + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef WPI_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; +#endif + } + } + +private: + using t_is_void = std::true_type; + using t_is_not_void = std::false_type; + using t_is_nothrow_move_constructible = std::true_type; + using move_constructing_t_can_throw = std::false_type; + using e_is_nothrow_move_constructible = std::true_type; + using move_constructing_e_can_throw = std::false_type; + + void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept { + // swapping void is a no-op + } + + void swap_where_both_have_value(expected &rhs, t_is_not_void) { + using std::swap; + swap(val(), rhs.val()); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept( + std::is_nothrow_move_constructible::value) { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { + swap_where_only_one_has_value_and_t_is_not_void( + rhs, typename std::is_nothrow_move_constructible::type{}, + typename std::is_nothrow_move_constructible::type{}); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + e_is_nothrow_move_constructible) noexcept { + auto temp = std::move(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + move_constructing_e_can_throw) { + auto temp = std::move(val()); + val().~T(); +#ifdef WPI_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + val() = std::move(temp); + throw; + } +#else + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, move_constructing_t_can_throw, + e_is_nothrow_move_constructible) { + auto temp = std::move(rhs.err()); + rhs.err().~unexpected_type(); +#ifdef WPI_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + rhs.err() = std::move(temp); + throw; + } +#else + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + +public: + template + detail::enable_if_t::value && + detail::is_swappable::value && + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> + swap(expected &rhs) noexcept( + std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value + &&std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value() && rhs.has_value()) { + swap_where_both_have_value(rhs, typename std::is_void::type{}); + } else if (!has_value() && rhs.has_value()) { + rhs.swap(*this); + } else if (has_value()) { + swap_where_only_one_has_value(rhs, typename std::is_void::type{}); + } else { + using std::swap; + swap(err(), rhs.err()); + } + } + + constexpr const T *operator->() const { + WPI_ASSERT(has_value()); + return valptr(); + } + WPI_EXPECTED_11_CONSTEXPR T *operator->() { + WPI_ASSERT(has_value()); + return valptr(); + } + + template ::value> * = nullptr> + constexpr const U &operator*() const & { + WPI_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + WPI_EXPECTED_11_CONSTEXPR U &operator*() & { + WPI_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + constexpr const U &&operator*() const && { + WPI_ASSERT(has_value()); + return std::move(val()); + } + template ::value> * = nullptr> + WPI_EXPECTED_11_CONSTEXPR U &&operator*() && { + WPI_ASSERT(has_value()); + return std::move(val()); + } + + constexpr bool has_value() const noexcept { return this->m_has_val; } + constexpr explicit operator bool() const noexcept { return this->m_has_val; } + + template ::value> * = nullptr> + WPI_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + WPI_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + WPI_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + template ::value> * = nullptr> + WPI_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + + constexpr const E &error() const & { + WPI_ASSERT(!has_value()); + return err().value(); + } + WPI_EXPECTED_11_CONSTEXPR E &error() & { + WPI_ASSERT(!has_value()); + return err().value(); + } + constexpr const E &&error() const && { + WPI_ASSERT(!has_value()); + return std::move(err().value()); + } + WPI_EXPECTED_11_CONSTEXPR E &&error() && { + WPI_ASSERT(!has_value()); + return std::move(err().value()); + } + + template constexpr T value_or(U &&v) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast(std::forward(v)); + } + template WPI_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move-constructible and convertible to from U&&"); + return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); + } +}; + +namespace detail { +template using exp_t = typename detail::decay_t::value_type; +template using err_t = typename detail::decay_t::error_type; +template using ret_t = expected>; + +#ifdef WPI_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#else +template struct TC; +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template ())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#endif + +#ifdef WPI_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} +#endif + +#if defined(WPI_EXPECTED_CXX14) && !defined(WPI_EXPECTED_GCC49) && \ + !defined(WPI_EXPECTED_GCC54) && !defined(WPI_EXPECTED_GCC55) +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#endif + +#ifdef WPI_EXPECTED_CXX14 +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#else +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#endif +} // namespace detail + +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); +} +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : true); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() == rhs.error() : false); +} + +template +constexpr bool operator==(const expected &x, const U &v) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator==(const U &v, const expected &x) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator!=(const expected &x, const U &v) { + return x.has_value() ? *x != v : true; +} +template +constexpr bool operator!=(const U &v, const expected &x) { + return x.has_value() ? *x != v : true; +} + +template +constexpr bool operator==(const expected &x, const unexpected &e) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator==(const unexpected &e, const expected &x) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator!=(const expected &x, const unexpected &e) { + return x.has_value() ? true : x.error() != e.value(); +} +template +constexpr bool operator!=(const unexpected &e, const expected &x) { + return x.has_value() ? true : x.error() != e.value(); +} + +template ::value || + std::is_move_constructible::value) && + detail::is_swappable::value && + std::is_move_constructible::value && + detail::is_swappable::value> * = nullptr> +void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} +} // namespace wpi + +#endif diff --git a/wpiutil/src/test/java/edu/wpi/first/util/struct/DynamicStructTest.java b/wpiutil/src/test/java/edu/wpi/first/util/struct/DynamicStructTest.java index 9e77c80aeea..78a8127f433 100644 --- a/wpiutil/src/test/java/edu/wpi/first/util/struct/DynamicStructTest.java +++ b/wpiutil/src/test/java/edu/wpi/first/util/struct/DynamicStructTest.java @@ -18,6 +18,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +@SuppressWarnings("AvoidEscapedUnicodeCharacters") class DynamicStructTest { @SuppressWarnings("MemberName") private StructDescriptorDatabase db; @@ -387,4 +388,120 @@ void testStandardArray( assertNotNull(field.getStruct()); } } + + @Test + void testStringAllZeros() { + var desc = assertDoesNotThrow(() -> db.add("test", "char a[32]")); + var dynamic = DynamicStruct.allocate(desc); + var field = desc.findFieldByName("a"); + assertEquals("", dynamic.getStringField(field)); + } + + @Test + void testStringRoundTrip() { + var desc = assertDoesNotThrow(() -> db.add("test", "char a[32]")); + var dynamic = DynamicStruct.allocate(desc); + var field = desc.findFieldByName("a"); + assertTrue(dynamic.setStringField(field, "abc")); + assertEquals("abc", dynamic.getStringField(field)); + } + + @Test + void testStringRoundTripEmbeddedNull() { + var desc = assertDoesNotThrow(() -> db.add("test", "char a[32]")); + var dynamic = DynamicStruct.allocate(desc); + var field = desc.findFieldByName("a"); + assertTrue(dynamic.setStringField(field, "ab\0c")); + assertEquals("ab\0c", dynamic.getStringField(field)); + } + + @Test + void testStringRoundTripStringTooLong() { + var desc = assertDoesNotThrow(() -> db.add("test", "char a[2]")); + var dynamic = DynamicStruct.allocate(desc); + var field = desc.findFieldByName("a"); + assertFalse(dynamic.setStringField(field, "abc")); + assertEquals("ab", dynamic.getStringField(field)); + } + + @Test + void testStringRoundTripPartial2ByteUtf8() { + var desc = assertDoesNotThrow(() -> db.add("test", "char a[2]")); + var dynamic = DynamicStruct.allocate(desc); + var field = desc.findFieldByName("a"); + assertFalse(dynamic.setStringField(field, "a\u0234")); + assertEquals("a", dynamic.getStringField(field)); + } + + @Test + void testStringRoundTrip2ByteUtf8() { + var desc = assertDoesNotThrow(() -> db.add("test", "char a[3]")); + var dynamic = DynamicStruct.allocate(desc); + var field = desc.findFieldByName("a"); + assertTrue(dynamic.setStringField(field, "a\u0234")); + assertEquals("a\u0234", dynamic.getStringField(field)); + } + + @Test + void testStringRoundTripPartial3ByteUtf8FirstByte() { + var desc = assertDoesNotThrow(() -> db.add("test", "char a[2]")); + var dynamic = DynamicStruct.allocate(desc); + var field = desc.findFieldByName("a"); + assertFalse(dynamic.setStringField(field, "a\u1234")); + assertEquals("a", dynamic.getStringField(field)); + } + + @Test + void testStringRoundTripPartial3ByteUtf8SecondByte() { + var desc = assertDoesNotThrow(() -> db.add("test", "char a[3]")); + var dynamic = DynamicStruct.allocate(desc); + var field = desc.findFieldByName("a"); + assertFalse(dynamic.setStringField(field, "a\u1234")); + assertEquals("a", dynamic.getStringField(field)); + } + + @Test + void testStringRoundTrip3ByteUtf8() { + var desc = assertDoesNotThrow(() -> db.add("test", "char a[4]")); + var dynamic = DynamicStruct.allocate(desc); + var field = desc.findFieldByName("a"); + assertTrue(dynamic.setStringField(field, "a\u1234")); + assertEquals("a\u1234", dynamic.getStringField(field)); + } + + @Test + void testStringRoundTripPartial4ByteUtf8FirstByte() { + var desc = assertDoesNotThrow(() -> db.add("test", "char a[2]")); + var dynamic = DynamicStruct.allocate(desc); + var field = desc.findFieldByName("a"); + assertFalse(dynamic.setStringField(field, "a\uD83D\uDC00")); + assertEquals("a", dynamic.getStringField(field)); + } + + @Test + void testStringRoundTripPartial4ByteUtf8SecondByte() { + var desc = assertDoesNotThrow(() -> db.add("test", "char a[3]")); + var dynamic = DynamicStruct.allocate(desc); + var field = desc.findFieldByName("a"); + assertFalse(dynamic.setStringField(field, "a\uD83D\uDC00")); + assertEquals("a", dynamic.getStringField(field)); + } + + @Test + void testStringRoundTripPartial4ByteUtf8ThirdByte() { + var desc = assertDoesNotThrow(() -> db.add("test", "char a[4]")); + var dynamic = DynamicStruct.allocate(desc); + var field = desc.findFieldByName("a"); + assertFalse(dynamic.setStringField(field, "a\uD83D\uDC00")); + assertEquals("a", dynamic.getStringField(field)); + } + + @Test + void testStringRoundTrip4ByteUtf8() { + var desc = assertDoesNotThrow(() -> db.add("test", "char a[5]")); + var dynamic = DynamicStruct.allocate(desc); + var field = desc.findFieldByName("a"); + assertTrue(dynamic.setStringField(field, "a\uD83D\uDC00")); + assertEquals("a\uD83D\uDC00", dynamic.getStringField(field)); + } } diff --git a/wpiutil/src/test/native/cpp/expected/ExpectedTest.cpp b/wpiutil/src/test/native/cpp/expected/ExpectedTest.cpp new file mode 100644 index 00000000000..f3c9150586d --- /dev/null +++ b/wpiutil/src/test/native/cpp/expected/ExpectedTest.cpp @@ -0,0 +1,58 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include +#include +#include + +#include + +#include "wpi/expected" + +namespace { +struct TakesInitAndVariadic { + std::vector v; + std::tuple t; + + template + TakesInitAndVariadic(std::initializer_list l, // NOLINT + Args&&... args) + : v(l), t(std::forward(args)...) {} +}; +} // namespace + +TEST(ExpectedTest, Emplace) { + { + wpi::expected, int> e; + e.emplace(new int{42}); + EXPECT_TRUE(e); + EXPECT_EQ(**e, 42); + } + + { + wpi::expected, int> e; + e.emplace({0, 1}); + EXPECT_TRUE(e); + EXPECT_EQ((*e)[0], 0); + EXPECT_EQ((*e)[1], 1); + } + + { + wpi::expected, int> e; + e.emplace(2, 3); + EXPECT_TRUE(e); + EXPECT_EQ(std::get<0>(*e), 2); + EXPECT_EQ(std::get<1>(*e), 3); + } + + { + wpi::expected e = wpi::make_unexpected(0); + e.emplace({0, 1}, 2, 3); + EXPECT_TRUE(e); + EXPECT_EQ(e->v[0], 0); + EXPECT_EQ(e->v[1], 1); + EXPECT_EQ(std::get<0>(e->t), 2); + EXPECT_EQ(std::get<1>(e->t), 3); + } +} diff --git a/wpiutil/src/test/native/cpp/struct/DynamicStructTest.cpp b/wpiutil/src/test/native/cpp/struct/DynamicStructTest.cpp index d5d21d17b6b..b0d43ffd069 100644 --- a/wpiutil/src/test/native/cpp/struct/DynamicStructTest.cpp +++ b/wpiutil/src/test/native/cpp/struct/DynamicStructTest.cpp @@ -269,6 +269,179 @@ TEST_F(DynamicStructTest, DuplicateFieldName) { ASSERT_EQ(err, "duplicate field a"); } +TEST_F(DynamicStructTest, StringAllZeros) { + auto desc = db.Add("test", "char a[32]", &err); + uint8_t data[32]; + std::memset(data, 0, sizeof(data)); + ASSERT_EQ(desc->GetSize(), sizeof(data) / sizeof(data[0])); + wpi::MutableDynamicStruct dynamic{desc, data}; + auto field = desc->FindFieldByName("a"); + EXPECT_EQ(dynamic.GetStringField(field), ""); +} + +TEST_F(DynamicStructTest, StringRoundTrip) { + auto desc = db.Add("test", "char a[32]", &err); + uint8_t data[32]; + std::memset(data, 0, sizeof(data)); + ASSERT_EQ(desc->GetSize(), sizeof(data) / sizeof(data[0])); + wpi::MutableDynamicStruct dynamic{desc, data}; + auto field = desc->FindFieldByName("a"); + EXPECT_TRUE(dynamic.SetStringField(field, "abc")); + EXPECT_EQ(dynamic.GetStringField(field), "abc"); +} + +TEST_F(DynamicStructTest, StringRoundTripEmbeddedNull) { + auto desc = db.Add("test", "char a[32]", &err); + uint8_t data[32]; + std::memset(data, 0, sizeof(data)); + ASSERT_EQ(desc->GetSize(), sizeof(data) / sizeof(data[0])); + wpi::MutableDynamicStruct dynamic{desc, data}; + auto field = desc->FindFieldByName("a"); + std::string check{"ab\0c", 4}; + ASSERT_EQ(check.size(), 4u); + EXPECT_TRUE(dynamic.SetStringField(field, check)); + auto get = dynamic.GetStringField(field); + EXPECT_EQ(get, check); + EXPECT_EQ(4u, get.size()); +} + +TEST_F(DynamicStructTest, StringRoundTripTooLong) { + auto desc = db.Add("test", "char a[2]", &err); + uint8_t data[2]; + std::memset(data, 0, sizeof(data)); + ASSERT_EQ(desc->GetSize(), sizeof(data) / sizeof(data[0])); + wpi::MutableDynamicStruct dynamic{desc, data}; + auto field = desc->FindFieldByName("a"); + EXPECT_FALSE(dynamic.SetStringField(field, "abc")); + auto get = dynamic.GetStringField(field); + EXPECT_EQ(get, "ab"); + EXPECT_EQ(2u, get.size()); +} + +TEST_F(DynamicStructTest, StringRoundTripPartial2ByteUtf8) { + auto desc = db.Add("test", "char a[2]", &err); + uint8_t data[2]; + std::memset(data, 0, sizeof(data)); + ASSERT_EQ(desc->GetSize(), sizeof(data) / sizeof(data[0])); + wpi::MutableDynamicStruct dynamic{desc, data}; + auto field = desc->FindFieldByName("a"); + EXPECT_FALSE(dynamic.SetStringField(field, "a\u0234")); + auto get = dynamic.GetStringField(field); + EXPECT_EQ(get, "a"); + EXPECT_EQ(1u, get.size()); +} + +TEST_F(DynamicStructTest, StringRoundTrip2ByteUtf8) { + auto desc = db.Add("test", "char a[3]", &err); + uint8_t data[3]; + std::memset(data, 0, sizeof(data)); + ASSERT_EQ(desc->GetSize(), sizeof(data) / sizeof(data[0])); + wpi::MutableDynamicStruct dynamic{desc, data}; + auto field = desc->FindFieldByName("a"); + EXPECT_TRUE(dynamic.SetStringField(field, "a\u0234")); + auto get = dynamic.GetStringField(field); + EXPECT_EQ(get, "a\u0234"); + EXPECT_EQ(3u, get.size()); +} + +TEST_F(DynamicStructTest, StringRoundTrip3ByteUtf8) { + auto desc = db.Add("test", "char a[4]", &err); + uint8_t data[4]; + std::memset(data, 0, sizeof(data)); + ASSERT_EQ(desc->GetSize(), sizeof(data) / sizeof(data[0])); + wpi::MutableDynamicStruct dynamic{desc, data}; + auto field = desc->FindFieldByName("a"); + EXPECT_TRUE(dynamic.SetStringField(field, "a\u1234")); + auto get = dynamic.GetStringField(field); + EXPECT_EQ(get, "a\u1234"); + EXPECT_EQ(4u, get.size()); +} + +TEST_F(DynamicStructTest, StringRoundTrip3ByteUtf8PartialFirstByte) { + auto desc = db.Add("test", "char a[2]", &err); + uint8_t data[2]; + std::memset(data, 0, sizeof(data)); + ASSERT_EQ(desc->GetSize(), sizeof(data) / sizeof(data[0])); + wpi::MutableDynamicStruct dynamic{desc, data}; + auto field = desc->FindFieldByName("a"); + EXPECT_FALSE(dynamic.SetStringField(field, "a\u1234")); + auto get = dynamic.GetStringField(field); + EXPECT_EQ(get, "a"); + EXPECT_EQ(1u, get.size()); +} + +TEST_F(DynamicStructTest, StringRoundTrip3ByteUtf8PartialSecondByte) { + auto desc = db.Add("test", "char a[3]", &err); + uint8_t data[3]; + std::memset(data, 0, sizeof(data)); + ASSERT_EQ(desc->GetSize(), sizeof(data) / sizeof(data[0])); + wpi::MutableDynamicStruct dynamic{desc, data}; + auto field = desc->FindFieldByName("a"); + EXPECT_FALSE(dynamic.SetStringField(field, "a\u1234")); + auto get = dynamic.GetStringField(field); + EXPECT_EQ(get, "a"); + EXPECT_EQ(1u, get.size()); +} + +// MSVC and GCC do surrogate pairs differently. +// Manually construct the 4 byte string +static constexpr char buffer[] = { + static_cast(0x61), static_cast(0xf0), static_cast(0x9f), + static_cast(0x90), static_cast(0x80), static_cast(0x00)}; +static constexpr std::string_view fourByteUtf8String{buffer}; + +TEST_F(DynamicStructTest, StringRoundTrip4ByteUtf8) { + auto desc = db.Add("test", "char a[5]", &err); + uint8_t data[5]; + std::memset(data, 0, sizeof(data)); + ASSERT_EQ(desc->GetSize(), sizeof(data) / sizeof(data[0])); + wpi::MutableDynamicStruct dynamic{desc, data}; + auto field = desc->FindFieldByName("a"); + EXPECT_TRUE(dynamic.SetStringField(field, fourByteUtf8String)); + auto get = dynamic.GetStringField(field); + EXPECT_EQ(get, fourByteUtf8String); + EXPECT_EQ(5u, get.size()); +} + +TEST_F(DynamicStructTest, StringRoundTrip4ByteUtf8PartialFirstByte) { + auto desc = db.Add("test", "char a[2]", &err); + uint8_t data[2]; + std::memset(data, 0, sizeof(data)); + ASSERT_EQ(desc->GetSize(), sizeof(data) / sizeof(data[0])); + wpi::MutableDynamicStruct dynamic{desc, data}; + auto field = desc->FindFieldByName("a"); + EXPECT_FALSE(dynamic.SetStringField(field, fourByteUtf8String)); + auto get = dynamic.GetStringField(field); + EXPECT_EQ(get, "a"); + EXPECT_EQ(1u, get.size()); +} + +TEST_F(DynamicStructTest, StringRoundTrip4ByteUtf8PartialSecondByte) { + auto desc = db.Add("test", "char a[3]", &err); + uint8_t data[3]; + std::memset(data, 0, sizeof(data)); + ASSERT_EQ(desc->GetSize(), sizeof(data) / sizeof(data[0])); + wpi::MutableDynamicStruct dynamic{desc, data}; + auto field = desc->FindFieldByName("a"); + EXPECT_FALSE(dynamic.SetStringField(field, fourByteUtf8String)); + auto get = dynamic.GetStringField(field); + EXPECT_EQ(get, "a"); + EXPECT_EQ(1u, get.size()); +} + +TEST_F(DynamicStructTest, StringRoundTrip4ByteUtf8PartialThirdByte) { + auto desc = db.Add("test", "char a[4]", &err); + uint8_t data[4]; + std::memset(data, 0, sizeof(data)); + ASSERT_EQ(desc->GetSize(), sizeof(data) / sizeof(data[0])); + wpi::MutableDynamicStruct dynamic{desc, data}; + auto field = desc->FindFieldByName("a"); + EXPECT_FALSE(dynamic.SetStringField(field, fourByteUtf8String)); + auto get = dynamic.GetStringField(field); + EXPECT_EQ(get, "a"); + EXPECT_EQ(1u, get.size()); +} + struct SimpleTestParam { const char* schema; size_t size; diff --git a/xrpVendordep/CMakeLists.txt b/xrpVendordep/CMakeLists.txt index 7a481947cef..52cd2e46aa8 100644 --- a/xrpVendordep/CMakeLists.txt +++ b/xrpVendordep/CMakeLists.txt @@ -66,12 +66,12 @@ target_include_directories( $ ) -install(TARGETS xrpVendordep EXPORT xrpVendordep DESTINATION "${main_lib_dest}") +install(TARGETS xrpVendordep EXPORT xrpvendordep DESTINATION "${main_lib_dest}") install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/xrpVendordep") -configure_file(xrpVendordep-config.cmake.in ${WPILIB_BINARY_DIR}/xrpVendordep-config.cmake) -install(FILES ${WPILIB_BINARY_DIR}/xrpVendordep-config.cmake DESTINATION share/xrpVendordep) -install(EXPORT xrpVendordep DESTINATION share/xrpVendordep) +configure_file(xrpvendordep-config.cmake.in ${WPILIB_BINARY_DIR}/xrpvendordep-config.cmake) +install(FILES ${WPILIB_BINARY_DIR}/xrpvendordep-config.cmake DESTINATION share/xrpVendordep) +install(EXPORT xrpvendordep DESTINATION share/xrpVendordep) if(WITH_TESTS) wpilib_add_test(xrpVendordep src/test/native/cpp) diff --git a/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPMotor.java b/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPMotor.java index 957f9e7a3c8..6db4fc236ac 100644 --- a/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPMotor.java +++ b/xrpVendordep/src/main/java/edu/wpi/first/wpilibj/xrp/XRPMotor.java @@ -17,6 +17,7 @@ * *

A SimDevice based motor controller representing the motors on an XRP robot */ +@SuppressWarnings("removal") public class XRPMotor implements MotorController { private static HashMap s_simDeviceNameMap = new HashMap<>(); private static HashSet s_registeredDevices = new HashSet<>(); diff --git a/xrpVendordep/src/main/native/cpp/xrp/XRPMotor.cpp b/xrpVendordep/src/main/native/cpp/xrp/XRPMotor.cpp index 02d1d587916..6d16f7b6a1b 100644 --- a/xrpVendordep/src/main/native/cpp/xrp/XRPMotor.cpp +++ b/xrpVendordep/src/main/native/cpp/xrp/XRPMotor.cpp @@ -6,6 +6,8 @@ #include +#include + using namespace frc; std::map XRPMotor::s_simDeviceMap = { @@ -27,6 +29,8 @@ void XRPMotor::CheckDeviceAllocation(int deviceNum) { s_registeredDevices.insert(deviceNum); } +WPI_IGNORE_DEPRECATED + XRPMotor::XRPMotor(int deviceNum) { CheckDeviceAllocation(deviceNum); @@ -42,6 +46,8 @@ XRPMotor::XRPMotor(int deviceNum) { } } +WPI_UNIGNORE_DEPRECATED + void XRPMotor::Set(double speed) { if (m_simSpeed) { bool invert = false; diff --git a/xrpVendordep/src/main/native/include/frc/xrp/XRPMotor.h b/xrpVendordep/src/main/native/include/frc/xrp/XRPMotor.h index c174cbf7e3d..8a220c08002 100644 --- a/xrpVendordep/src/main/native/include/frc/xrp/XRPMotor.h +++ b/xrpVendordep/src/main/native/include/frc/xrp/XRPMotor.h @@ -12,9 +12,12 @@ #include #include +#include namespace frc { +WPI_IGNORE_DEPRECATED + class XRPMotor : public frc::MotorController, public frc::MotorSafety { public: explicit XRPMotor(int deviceNum); @@ -43,4 +46,6 @@ class XRPMotor : public frc::MotorController, public frc::MotorSafety { static void CheckDeviceAllocation(int deviceNum); }; +WPI_UNIGNORE_DEPRECATED + } // namespace frc diff --git a/xrpVendordep/src/test/native/cpp/main.cpp b/xrpVendordep/src/test/native/cpp/main.cpp index 2d710be58f7..a2b90c59137 100644 --- a/xrpVendordep/src/test/native/cpp/main.cpp +++ b/xrpVendordep/src/test/native/cpp/main.cpp @@ -1,10 +1,10 @@ -// 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 - -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} +// 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 + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/xrpVendordep/xrpVendordep-config.cmake.in b/xrpVendordep/xrpVendordep-config.cmake.in deleted file mode 100644 index 42876940023..00000000000 --- a/xrpVendordep/xrpVendordep-config.cmake.in +++ /dev/null @@ -1,14 +0,0 @@ -include(CMakeFindDependencyMacro) - @WPIUTIL_DEP_REPLACE@ - @NTCORE_DEP_REPLACE@ - @CSCORE_DEP_REPLACE@ - @CAMERASERVER_DEP_REPLACE@ - @HAL_DEP_REPLACE@ - @WPILIBC_DEP_REPLACE@ - @WPIMATH_DEP_REPLACE@ - - @FILENAME_DEP_REPLACE@ - include(${SELF_DIR}/xrpVendordep.cmake) - if(@WITH_JAVA@) - include(${SELF_DIR}/xrpVendordep_jar.cmake) - endif() diff --git a/xrpVendordep/xrpvendordep-config.cmake.in b/xrpVendordep/xrpvendordep-config.cmake.in new file mode 100644 index 00000000000..8cb66bb1148 --- /dev/null +++ b/xrpVendordep/xrpvendordep-config.cmake.in @@ -0,0 +1,14 @@ +include(CMakeFindDependencyMacro) +@WPIUTIL_DEP_REPLACE@ +@NTCORE_DEP_REPLACE@ +@CSCORE_DEP_REPLACE@ +@CAMERASERVER_DEP_REPLACE@ +@HAL_DEP_REPLACE@ +@WPILIBC_DEP_REPLACE@ +@WPIMATH_DEP_REPLACE@ + +@FILENAME_DEP_REPLACE@ +include(${SELF_DIR}/xrpvendordep.cmake) +if(@WITH_JAVA@) + include(${SELF_DIR}/xrpVendordep_jar.cmake) +endif()