Compare commits

...

58 Commits

Author SHA1 Message Date
Dean Brettle
3116f790ea [sysid] Fix wrong position Kd with unnormalized time (#6433) 2024-03-12 21:49:28 -07:00
Tyler Veness
0e013dc021 [sysid] Fix "Sample" docs typo (NFC) (#6435) 2024-03-11 20:23:03 -07:00
sciencewhiz
f74f6f1d42 [docs] Add docs for features not supported on PDH (NFC) (#6436) 2024-03-11 20:22:33 -07:00
Peter Johnson
11c60df3e0 [hal] Use SIGKILL instead of SIGILL (#6431)
Fix typo.
2024-03-11 09:43:33 -07:00
Peter Johnson
3d9152a461 [hal] Raise SIGKILL instead of calling abort() (#6427)
We don't need to generate a core dump here if core dumps are enabled.
2024-03-10 20:32:54 -07:00
Peter Johnson
fbd239d15e [sim] GUI: Use shift to enable docking features (#6429) 2024-03-10 20:29:20 -07:00
Tyler Veness
7cd4a75323 [sysid] Fix crash on negative feedforward gains (#6425)
LinearSystemId's linear system factories throw on negative feedforward
gains, but SysId can compute the feedback gains just fine in that case.
Now we construct the system manually instead.

Fixes #6423.
2024-03-10 17:43:02 -07:00
Tyler Veness
973bb55e66 [wpimath] LinearSystemId: Don't throw if Kv = 0 (#6424)
That's just a system with no back-EMF.
2024-03-10 08:11:18 -07:00
DeltaDizzy
7bd8c44570 [wpimath] Add structured data support for DifferentialDriveWheelPositions (#6412) 2024-03-09 10:09:02 -08:00
Peter Johnson
18e57f7872 [wpilibj] Call abort() on Rio on caught Java exception (#6420)
On Rio, we simply want to restart the robot program as quickly as possible,
and don't want to risk a hang somewhere that will keep that from happening.

The main downside of this is it won't wait for threads to finish (e.g. data logs won't get a final flush).
2024-03-09 09:55:49 -08:00
Tyler Veness
ccb4cbed63 [sysid] Fix arm characterization crash (#6422)
Fixes #6421.
2024-03-09 09:54:37 -08:00
Thad House
c19ee8b0fe [wpiutil, hal] Crash on failure for SetupNowRio() and wpi::Now() when not configured (#6417)
This is an unrecoverable condition, so always terminate.
2024-03-08 00:26:53 -08:00
Tyler Veness
e64c20346d [examples] Remove unused private variables (#6403) 2024-02-28 19:58:15 -08:00
Peter Johnson
f1a1ffd7fc [wpiutil] Rate-limit FPGA error from Now() (#6394) 2024-02-25 11:25:46 -08:00
Peter Johnson
c27ddf5ef9 [ci] Windows cmake: update vcpkg version (#6397)
We need fmtlib 10.2.1 to work around a compiler bug.

Also, reducing the number of jobs is no longer required with Actions runner upgrades.
2024-02-24 18:55:10 -08:00
Dean Brettle
8b669330eb [sysid] Fix position feedback latency compensation (#6392) 2024-02-23 14:13:43 -08:00
Tyler Veness
ca6e307ea5 [ci] Upgrade wpiformat (#6395) 2024-02-23 14:12:28 -08:00
DeltaDizzy
607682b687 [wpiunits] Fix Distance class javadocs to state the correct dimension (NFC) (#6363) 2024-02-19 12:58:56 -08:00
Peter Johnson
4b94a64b06 [glass] Fix FMS game data display and editing (#6381)
Also don't require Enter for editing game data or match time.
2024-02-18 16:29:58 -08:00
Thad House
63d9e945b8 [hal] HAL_RefreshDSData: Zero out control word on DS disconnect, use cache in sim (#6380) 2024-02-18 14:30:40 -08:00
Joseph Eng
0ad6b3acb3 [apriltag] Add AprilTagFieldLayout.loadField() (#6377) 2024-02-17 21:12:59 -08:00
Eli Barnett
02aed35c6e [sysid] Relax peak acceleration search (#6378) 2024-02-17 21:12:00 -08:00
Peter Johnson
a8a352ed8c [ntcore] Add hidden subscribe option (#6376)
This allows creating subscribers that aren't communicated with the network.
2024-02-17 00:40:14 -08:00
Peter Johnson
0cdab55e5b [ntcore] Don't send value update to client setting value (#6375) 2024-02-16 14:18:07 -08:00
Thad House
ba15844c28 [hal,wpiutil] Error out of HAL_Initialize if SetupRioNow fails (#6374) 2024-02-15 22:57:06 -08:00
shueja
6afff99640 [wpimath] ExponentialProfile: Return copy of input state (#6370)
As State is mutable, this avoids accidental modification of the passed-in object by the caller modifying the return value.
2024-02-15 16:32:51 -08:00
Tyler Veness
d4d0545dc1 [apriltag] Fix field length in 2024 JSON (#6373)
Fixes #6371
2024-02-15 10:42:03 -08:00
Joe Wildfong
6b6a55b72e [build] Fix tcpsockets header publishing (#6367) 2024-02-12 19:44:31 -08:00
fodfodfod
1e168f363e [wpimath] Feed forwards: Use correct 'k' value in error message (#6360) 2024-02-11 10:42:04 -08:00
DeltaDizzy
da3abade83 [examples] Add angular subsystem to SysIdRoutine example (#6297)
Co-authored-by: Tim Winters <twinters@wpi.edu>
2024-02-10 10:44:57 -08:00
Asa Paparo
62cba9a4d3 [wpimath] Add vector projection and geometry vector conversions (#6343) 2024-02-10 10:43:58 -08:00
N0tACyb0rg
3207795d0d [wpimath] Add lastValue() method to filters (#6351) 2024-02-10 10:43:23 -08:00
Joseph Eng
e506e09a06 [wpilibc] Const-qualify SendableChooser::GetSelected() (#6356) 2024-02-10 10:42:53 -08:00
Joseph Eng
163f7ee704 [wpilibc] SendableChooser: Remove unusable std::unique_ptr case (#6357) 2024-02-10 10:41:57 -08:00
Thad House
e9c744c456 [wpimath] Quaternion::Log(): Remove duplicate calls to norm() (#6358) 2024-02-10 10:41:19 -08:00
Kython89
300419c151 [wpilibc] SysIdRoutineLog: Initialize m_stateInitialized (#6359)
This caused non-deterministic behavior as to if the `sysid-test-state-` will appear in the log.
2024-02-10 10:40:38 -08:00
Tyler Veness
1db3936965 [wpimath] Remove unused include from RamseteController.cpp (#6346) 2024-02-05 22:43:50 -08:00
Ryan Blue
6cc7e52de7 [commands] TrapezoidProfileSubsystem: Fix incorrect ordering of parameters (#6338) 2024-02-01 20:24:43 -08:00
Sam Carlberg
d4533a8900 [wpilibj] AddressableLEDBuffer: Add methods for reading individual RGB values (#6333)
This avoids the allocation/GC overhead of returning a Color8 value.

Also add an indexed iterator forEach to loop over the entire buffer.
2024-02-01 14:01:53 -08:00
vichik
90bb6cfffa [wpiunits] Fix measure isNear function (#6313)
Now the function allows comparison between negative numbers, positive numbers or both.
2024-01-31 13:18:47 -08:00
Ryan Blue
cb094e4ff6 [examples] Don't reset encoders when resetting odometry (#6329) 2024-01-31 13:18:07 -08:00
Isaac Turner
60c6ed9812 [ci] Bump python, java and react script versions (#6325) 2024-01-31 13:17:41 -08:00
Tim Winters
ee15cc172a [commands] Reset timer in quasistatic SysIdRoutine test (#6322) 2024-01-27 23:51:00 -08:00
Joseph Eng
1016e95242 [examples] Fix memory leak in C++ controller command examples (#6306) 2024-01-27 23:49:41 -08:00
Sam Carlberg
19f1903959 [wpiunits] Add singularized aliases for built in units (#6323) 2024-01-27 23:47:59 -08:00
DeltaDizzy
53ebb6679e [examples] Move triggers to subsystem fields (#6318) 2024-01-27 23:47:06 -08:00
Tyler Veness
177132fa2a Replace C++ unit .to<double>() with .value() (#6317)
The latter is shorter and is what we use everywhere else.
2024-01-27 07:58:25 -08:00
Thad House
bbb230491a Force cpp files to be LF line endings (#6319)
Just marking a file as text will cause git to override the local core.autocrlf setting, and assume you want it to be true, which isn't what you want.
2024-01-26 23:20:51 -08:00
Tyler Veness
84ef71ace0 [wpimath] Make Rotation2d implicitly convert from any angle unit (#6316)
Add unit category concepts to support this.
2024-01-26 12:49:22 -08:00
Tyler Veness
68736d802d [wpimath] Clean up profile classes (#6311)
* Reorder functions so they match between languages
* Copy more complete JavaDocs to C++
* Fix incorrect description for time parameter of
  TrapezoidProfile.calculate()
2024-01-25 22:22:42 -08:00
Tyler Veness
d895a0c09f [wpiutil] Add std::expected shim (#6310)
Its tests use Catch2 instead of GoogleTest, so we can't import them.
2024-01-25 22:21:37 -08:00
sciencewhiz
64a9d413bf Update contributing for addition of Python (NFC) (#6307) 2024-01-25 21:31:57 -08:00
Chris Gerth
a70e83ae2e [glass] Update field size defaults in Field2D.cpp (#6298)
Looks like the field length is longer in 2024. Used the onshape model to measure the size.
2024-01-23 21:28:17 -08:00
Isaac Turner
47652d7a3c [commands] Remove unused headers (#6300) 2024-01-23 21:27:24 -08:00
Tyler Veness
be78552db7 [sysid] Fix SSTO calculation (#6301) 2024-01-23 21:26:49 -08:00
Peter Johnson
3acae550d6 [ntcore] StructArrayTopic: Publish schema in span-taking setters (#6303) 2024-01-23 21:26:06 -08:00
Thad House
9d55941ce5 [build] Fix macOS apps not always being an application (#6286) 2024-01-21 20:41:08 -08:00
Peter Johnson
51d92c7027 [build] Fix compilation with musl (#6289) 2024-01-21 20:32:56 -08:00
172 changed files with 5129 additions and 659 deletions

6
.gitattributes vendored
View File

@@ -1,7 +1,7 @@
*.cpp text
*.cpp text eol=lf
*.gradle text eol=lf
*.h text
*.inc text
*.h text eol=lf
*.inc text eol=lf
*.java text eol=lf
*.json text eol=lf
*.md text eol=lf

View File

@@ -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
@@ -88,28 +88,13 @@ jobs:
uses: lukka/run-vcpkg@v11.1
with:
vcpkgDirectory: ${{ runner.workspace }}/vcpkg
vcpkgGitCommitId: 78b61582c9e093fda56a01ebb654be15a0033897 # HEAD on 2023-08-6
vcpkgGitCommitId: 37c3e63a1306562f7f59c4c3c8892ddd50fdf992 # HEAD on 2024-02-24
- name: configure
run: cmake -S . -B build -G "Ninja" -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_BUILD_TYPE=Release -DWITH_JAVA=OFF -DWITH_EXAMPLES=ON -DUSE_SYSTEM_FMTLIB=ON -DUSE_SYSTEM_LIBUV=ON -DUSE_SYSTEM_EIGEN=ON -DCMAKE_TOOLCHAIN_FILE=${{ runner.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_INSTALL_OPTIONS=--clean-after-build -DVCPKG_TARGET_TRIPLET=x64-windows-release -DVCPKG_HOST_TRIPLET=x64-windows-release
run: cmake -S . -B build -G "Ninja" -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_BUILD_TYPE=Release -DWITH_JAVA=OFF -DWITH_EXAMPLES=ON -DUSE_SYSTEM_FMTLIB=ON -DUSE_SYSTEM_LIBUV=ON -DUSE_SYSTEM_EIGEN=OFF -DCMAKE_TOOLCHAIN_FILE=${{ runner.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_INSTALL_OPTIONS=--clean-after-build -DVCPKG_TARGET_TRIPLET=x64-windows-release -DVCPKG_HOST_TRIPLET=x64-windows-release
env:
SCCACHE_GHA_ENABLED: "true"
# Build wpiutil at full speed, wpimath depends on wpiutil
- name: build wpiutil
working-directory: build
run: cmake --build . --parallel $(nproc) --target wpiutil/all
env:
SCCACHE_GHA_ENABLED: "true"
# Build wpimath slow to prevent OOM
- name: build wpimath
working-directory: build
run: cmake --build . --parallel 1 --target wpimath/all
env:
SCCACHE_GHA_ENABLED: "true"
# Build everything else fast
- name: build
working-directory: build
run: cmake --build . --parallel $(nproc)

View File

@@ -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,11 +34,11 @@ 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: 17

View File

@@ -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

View File

@@ -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,7 +171,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
@@ -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'))

View File

@@ -23,11 +23,11 @@ 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
run: pip3 install wpiformat==2023.36
run: pip3 install wpiformat==2024.32
- name: Run
run: wpiformat
- name: Check output
@@ -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,7 +105,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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -82,6 +82,7 @@ def main():
# Write JSON
with open(filename.replace(".csv", ".json"), "w") as f:
json.dump(json_data, f, indent=2)
f.write("\n")
if __name__ == "__main__":

View File

@@ -16,6 +16,7 @@ import edu.wpi.first.math.geometry.Translation3d;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
@@ -221,6 +222,22 @@ public class AprilTagFieldLayout {
new ObjectMapper().writeValue(path.toFile(), this);
}
/**
* Get an official {@link AprilTagFieldLayout}.
*
* @param field The loadable AprilTag field layout.
* @return AprilTagFieldLayout of the field.
* @throws UncheckedIOException If the layout does not exist.
*/
public static AprilTagFieldLayout loadField(AprilTagFields field) {
try {
return loadFromResource(field.m_resourceFile);
} catch (IOException e) {
throw new UncheckedIOException(
"Could not load AprilTagFieldLayout from " + field.m_resourceFile, e);
}
}
/**
* Deserializes a field layout from a resource within a internal jar file.
*

View File

@@ -4,7 +4,6 @@
package edu.wpi.first.apriltag;
import java.io.IOException;
import java.io.UncheckedIOException;
/** Loadable AprilTag field layouts. */
@@ -36,11 +35,6 @@ public enum AprilTagFields {
* @throws UncheckedIOException If the layout does not exist
*/
public AprilTagFieldLayout loadAprilTagLayoutField() {
try {
return AprilTagFieldLayout.loadFromResource(m_resourceFile);
} catch (IOException e) {
throw new UncheckedIOException(
"Could not load AprilTagFieldLayout from " + m_resourceFile, e);
}
return AprilTagFieldLayout.loadField(this);
}
}

View File

@@ -125,3 +125,37 @@ void frc::from_json(const wpi::json& json, AprilTagFieldLayout& layout) {
layout.m_fieldWidth =
units::meter_t{json.at("field").at("width").get<double>()};
}
// Use namespace declaration for forward declaration
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();
} // namespace frc
AprilTagFieldLayout AprilTagFieldLayout::LoadField(AprilTagField field) {
std::string_view fieldString;
switch (field) {
case AprilTagField::k2022RapidReact:
fieldString = GetResource_2022_rapidreact_json();
break;
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");
}
wpi::json json = wpi::json::parse(fieldString);
return json.get<AprilTagFieldLayout>();
}
AprilTagFieldLayout frc::LoadAprilTagLayoutField(AprilTagField field) {
return AprilTagFieldLayout::LoadField(field);
}

View File

@@ -1,36 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "frc/apriltag/AprilTagFields.h"
#include <wpi/json.h>
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;
switch (field) {
case AprilTagField::k2022RapidReact:
fieldString = GetResource_2022_rapidreact_json();
break;
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");
}
wpi::json json = wpi::json::parse(fieldString);
return json.get<AprilTagFieldLayout>();
}
} // namespace frc

View File

@@ -14,6 +14,7 @@
#include <wpi/json_fwd.h>
#include "frc/apriltag/AprilTag.h"
#include "frc/apriltag/AprilTagFields.h"
#include "frc/geometry/Pose3d.h"
namespace frc {
@@ -48,6 +49,14 @@ class WPILIB_DLLEXPORT AprilTagFieldLayout {
kRedAllianceWallRightSide,
};
/**
* Loads an AprilTagFieldLayout from a predefined field
*
* @param field The predefined field
* @return AprilTagFieldLayout of the field
*/
static AprilTagFieldLayout LoadField(AprilTagField field);
AprilTagFieldLayout() = default;
/**
@@ -152,4 +161,13 @@ void to_json(wpi::json& json, const AprilTagFieldLayout& layout);
WPILIB_DLLEXPORT
void from_json(const wpi::json& json, AprilTagFieldLayout& layout);
/**
* Loads an AprilTagFieldLayout from a predefined field
*
* @param field The predefined field
* @return AprilTagFieldLayout of the field
*/
WPILIB_DLLEXPORT AprilTagFieldLayout
LoadAprilTagLayoutField(AprilTagField field);
} // namespace frc

View File

@@ -8,8 +8,6 @@
#include <wpi/SymbolExports.h>
#include "frc/apriltag/AprilTagFieldLayout.h"
namespace frc {
/**
@@ -28,12 +26,4 @@ enum class AprilTagField {
kNumFields,
};
/**
* Loads an AprilTagFieldLayout from a predefined field
*
* @param field The predefined field
*/
WPILIB_DLLEXPORT AprilTagFieldLayout
LoadAprilTagLayoutField(AprilTagField field);
} // namespace frc

View File

@@ -290,7 +290,7 @@
}
],
"field": {
"length": 16.451,
"length": 16.541,
"width": 8.211
}
}

View File

@@ -22,13 +22,14 @@ class LoadConfigTest {
@ParameterizedTest
@EnumSource(AprilTagFields.class)
void testLoad(AprilTagFields field) {
AprilTagFieldLayout layout = Assertions.assertDoesNotThrow(field::loadAprilTagLayoutField);
AprilTagFieldLayout layout =
Assertions.assertDoesNotThrow(() -> AprilTagFieldLayout.loadField(field));
assertNotNull(layout);
}
@Test
void test2022RapidReact() {
AprilTagFieldLayout layout = AprilTagFields.k2022RapidReact.loadAprilTagLayoutField();
AprilTagFieldLayout layout = AprilTagFieldLayout.loadField(AprilTagFields.k2022RapidReact);
// Blue Hangar Truss - Hub
Pose3d expectedPose =

View File

@@ -4,6 +4,7 @@
#include <gtest/gtest.h>
#include "frc/apriltag/AprilTagFieldLayout.h"
#include "frc/apriltag/AprilTagFields.h"
namespace frc {
@@ -20,7 +21,7 @@ std::vector<AprilTagField> GetAllFields() {
TEST(AprilTagFieldsTest, TestLoad2022RapidReact) {
AprilTagFieldLayout layout =
LoadAprilTagLayoutField(AprilTagField::k2022RapidReact);
AprilTagFieldLayout::LoadField(AprilTagField::k2022RapidReact);
// Blue Hangar Truss - Hub
auto expectedPose =
@@ -53,7 +54,7 @@ class AllFieldsFixtureTest : public ::testing::TestWithParam<AprilTagField> {};
TEST_P(AllFieldsFixtureTest, CheckEntireEnum) {
AprilTagField field = GetParam();
EXPECT_NO_THROW(LoadAprilTagLayoutField(field));
EXPECT_NO_THROW(AprilTagFieldLayout::LoadField(field));
}
INSTANTIATE_TEST_SUITE_P(ValuesEnumTestInstTests, AllFieldsFixtureTest,

View File

@@ -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

View File

@@ -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

View File

@@ -5,6 +5,7 @@
#include "glass/other/FMS.h"
#include <imgui.h>
#include <imgui_stdlib.h>
#include <wpi/SmallString.h>
#include "glass/DataSource.h"
@@ -58,8 +59,7 @@ void glass::DisplayFMS(FMSModel* model, bool editableDsAttached) {
if (auto data = model->GetMatchTimeData()) {
double val = data->GetValue();
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
if (ImGui::InputDouble("Match Time", &val, 0, 0, "%.1f",
ImGuiInputTextFlags_EnterReturnsTrue)) {
if (ImGui::InputDouble("Match Time", &val, 0, 0, "%.1f")) {
model->SetMatchTime(val);
}
data->EmitDrag();
@@ -78,16 +78,12 @@ void glass::DisplayFMS(FMSModel* model, bool editableDsAttached) {
}
// Game Specific Message
// make buffer full 64 width, null terminated, for editability
wpi::SmallString<64> gameSpecificMessage;
model->GetGameSpecificMessage(gameSpecificMessage);
gameSpecificMessage.resize(63);
gameSpecificMessage.push_back('\0');
wpi::SmallString<64> gameSpecificMessageBuf;
std::string gameSpecificMessage{
model->GetGameSpecificMessage(gameSpecificMessageBuf)};
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
if (ImGui::InputText("Game Specific", gameSpecificMessage.data(),
gameSpecificMessage.size(),
ImGuiInputTextFlags_EnterReturnsTrue)) {
model->SetGameSpecificMessage(gameSpecificMessage.data());
if (ImGui::InputText("Game Specific", &gameSpecificMessage)) {
model->SetGameSpecificMessage(gameSpecificMessage);
}
}
@@ -151,9 +147,10 @@ void glass::DisplayFMSReadOnly(FMSModel* model) {
}
}
wpi::SmallString<64> gameSpecificMessage;
model->GetGameSpecificMessage(gameSpecificMessage);
ImGui::Text("Game Specific: %s", exists ? gameSpecificMessage.c_str() : "?");
wpi::SmallString<64> gameSpecificMessageBuf;
std::string_view gameSpecificMessage =
model->GetGameSpecificMessage(gameSpecificMessageBuf);
ImGui::Text("Game Specific: %s", exists ? gameSpecificMessage.data() : "?");
if (!exists) {
ImGui::PopStyleColor();

View File

@@ -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);

View File

@@ -75,6 +75,9 @@ public final class HAL extends JNIWrapper {
*/
public static native void exitMain();
/** Terminates the executable (at the native level). Does nothing in simulation. */
public static native void terminate();
private static native void simPeriodicBeforeNative();
private static final List<Runnable> s_simPeriodicBefore = new ArrayList<>();

View File

@@ -83,6 +83,8 @@ public class PowerDistributionJNI extends JNIWrapper {
/**
* Gets the temperature of the PowerDistribution.
*
* <p>Not supported on the Rev PDH and returns 0.
*
* @param handle the module handle
* @return the module temperature (celsius)
* @see "HAL_GetPowerDistributionTemperature"
@@ -129,7 +131,9 @@ public class PowerDistributionJNI extends JNIWrapper {
public static native double getTotalCurrent(int handle);
/**
* Gets the total power of the PowerDistribution.
* Gets the total power of the Power Distribution Panel.
*
* <p>Not supported on the Rev PDH and returns 0.
*
* @param handle the module handle
* @return the total power (watts)
@@ -138,7 +142,9 @@ public class PowerDistributionJNI extends JNIWrapper {
public static native double getTotalPower(int handle);
/**
* Gets the total energy of the PowerDistribution.
* Gets the total energy of the Power Distribution Panel.
*
* <p>Not supported on the Rev PDH and does nothing.
*
* @param handle the module handle
* @return the total energy (joules)
@@ -147,7 +153,9 @@ public class PowerDistributionJNI extends JNIWrapper {
public static native double getTotalEnergy(int handle);
/**
* Resets the PowerDistribution accumulated energy.
* Resets the Power Distribution Panel accumulated energy.
*
* <p>Not supported on the Rev PDH and returns 0.
*
* @param handle the module handle
* @see "HAL_ClearPowerDistributionStickyFaults"

View File

@@ -539,15 +539,25 @@ HAL_Bool HAL_RefreshDSData(void) {
}
// If newest state shows we have a DS attached, just use the
// control word out of the cache, As it will be the one in sync
// with the data. Otherwise use the state that shows disconnected.
if (controlWord.dsAttached) {
newestControlWord = currentRead->controlWord;
} else {
// Zero out the control word. When the DS has never been connected
// this returns garbage. And there is no way we can detect that.
std::memset(&controlWord, 0, sizeof(controlWord));
newestControlWord = controlWord;
// with the data. If no data has been updated, at this point,
// and a DS wasn't attached previously, this will still return
// a zeroed out control word, with is the correct state for
// no new data.
if (!controlWord.dsAttached) {
// If the DS is not attached, we need to zero out the control word.
// This is because HAL_RefreshDSData is called asynchronously from
// the DS data. The dsAttached variable comes directly from netcomm
// and could be updated before the caches are. If that happens,
// we would end up returning the previous cached control word,
// which is out of sync with the current control word and could
// break invariants such as which alliance station is in used.
// Also, when the DS has never been connected the rest of the fields
// in control word are garbage, so we also need to zero out in that
// case too
std::memset(&currentRead->controlWord, 0,
sizeof(currentRead->controlWord));
}
newestControlWord = currentRead->controlWord;
}
uint32_t mask = tcpMask.exchange(0);

View File

@@ -524,7 +524,7 @@ static bool killExistingProgram(int timeout, int mode) {
return true;
}
static void SetupNowRio(void) {
static bool SetupNowRio(void) {
nFPGA::nRoboRIO_FPGANamespace::g_currentTargetClass =
nLoadOut::getTargetClass();
@@ -534,13 +534,13 @@ static void SetupNowRio(void) {
status = dladdr(reinterpret_cast<void*>(tHMB::create), &info);
if (status == 0) {
fmt::print(stderr, "Failed to call dladdr on chipobject {}\n", dlerror());
return;
return false;
}
void* chipObjectLibrary = dlopen(info.dli_fname, RTLD_LAZY);
if (chipObjectLibrary == nullptr) {
fmt::print(stderr, "Failed to call dlopen on chipobject {}\n", dlerror());
return;
return false;
}
std::unique_ptr<tHMB> hmb;
@@ -548,9 +548,9 @@ static void SetupNowRio(void) {
if (hmb == nullptr) {
fmt::print(stderr, "Failed to open HMB on chipobject {}\n", status);
dlclose(chipObjectLibrary);
return;
return false;
}
wpi::impl::SetupNowRio(chipObjectLibrary, std::move(hmb));
return wpi::impl::SetupNowRio(chipObjectLibrary, std::move(hmb));
}
HAL_Bool HAL_Initialize(int32_t timeout, int32_t mode) {
@@ -593,7 +593,17 @@ HAL_Bool HAL_Initialize(int32_t timeout, int32_t mode) {
setNewDataSem(nullptr);
});
SetupNowRio();
if (!SetupNowRio()) {
fmt::print(stderr,
"Failed to run SetupNowRio(). This is a fatal error. The "
"process is being terminated.\n");
std::fflush(stderr);
// Attempt to force a segfault to get a better java log
*reinterpret_cast<int*>(0) = 0;
// If that fails, terminate
std::terminate();
return false;
}
int32_t status = 0;

View File

@@ -6,6 +6,10 @@
#include <jni.h>
#ifdef __FRC_ROBORIO__
#include <signal.h>
#endif
#include <cassert>
#include <cstring>
@@ -82,6 +86,20 @@ Java_edu_wpi_first_hal_HAL_exitMain
HAL_ExitMain();
}
/*
* Class: edu_wpi_first_hal_HAL
* Method: terminate
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_HAL_terminate
(JNIEnv*, jclass)
{
#ifdef __FRC_ROBORIO__
::raise(SIGKILL);
#endif
}
/*
* Class: edu_wpi_first_hal_HAL
* Method: simPeriodicBeforeNative

View File

@@ -101,7 +101,9 @@ int32_t HAL_GetPowerDistributionNumChannels(HAL_PowerDistributionHandle handle,
int32_t* status);
/**
* Gets the temperature of the PowerDistribution.
* Gets the temperature of the Power Distribution Panel.
*
* Not supported on the Rev PDH and returns 0.
*
* @param[in] handle the module handle
* @param[out] status Error status variable. 0 on success.
@@ -156,7 +158,9 @@ double HAL_GetPowerDistributionTotalCurrent(HAL_PowerDistributionHandle handle,
int32_t* status);
/**
* Gets the total power of the PowerDistribution.
* Gets the total power of the Power Distribution Panel.
*
* Not supported on the Rev PDH and returns 0.
*
* @param[in] handle the module handle
* @param[out] status Error status variable. 0 on success.
@@ -166,7 +170,9 @@ double HAL_GetPowerDistributionTotalPower(HAL_PowerDistributionHandle handle,
int32_t* status);
/**
* Gets the total energy of the PowerDistribution.
* Gets the total energy of the Power Distribution Panel.
*
* Not supported on the Rev PDH and returns 0.
*
* @param[in] handle the module handle
* @param[out] status Error status variable. 0 on success.
@@ -178,6 +184,8 @@ double HAL_GetPowerDistributionTotalEnergy(HAL_PowerDistributionHandle handle,
/**
* Resets the PowerDistribution accumulated energy.
*
* Not supported on the Rev PDH and does nothing.
*
* @param[in] handle the module handle
* @param[out] status Error status variable. 0 on success.
*/

View File

@@ -44,6 +44,7 @@ struct JoystickDataCache {
HAL_JoystickButtons buttons[kJoystickPorts];
HAL_AllianceStationID allianceStation;
double matchTime;
HAL_ControlWord controlWord;
};
static_assert(std::is_standard_layout_v<JoystickDataCache>);
// static_assert(std::is_trivial_v<JoystickDataCache>);
@@ -65,6 +66,16 @@ void JoystickDataCache::Update() {
}
allianceStation = SimDriverStationData->allianceStationId;
matchTime = SimDriverStationData->matchTime;
HAL_ControlWord tmpControlWord;
std::memset(&tmpControlWord, 0, sizeof(tmpControlWord));
tmpControlWord.enabled = SimDriverStationData->enabled;
tmpControlWord.autonomous = SimDriverStationData->autonomous;
tmpControlWord.test = SimDriverStationData->test;
tmpControlWord.eStop = SimDriverStationData->eStop;
tmpControlWord.fmsAttached = SimDriverStationData->fmsAttached;
tmpControlWord.dsAttached = SimDriverStationData->dsAttached;
this->controlWord = tmpControlWord;
}
#define CHECK_JOYSTICK_NUMBER(stickNum) \
@@ -322,20 +333,32 @@ HAL_Bool HAL_RefreshDSData(void) {
if (gShutdown) {
return false;
}
HAL_ControlWord controlWord;
std::memset(&controlWord, 0, sizeof(controlWord));
controlWord.enabled = SimDriverStationData->enabled;
controlWord.autonomous = SimDriverStationData->autonomous;
controlWord.test = SimDriverStationData->test;
controlWord.eStop = SimDriverStationData->eStop;
controlWord.fmsAttached = SimDriverStationData->fmsAttached;
controlWord.dsAttached = SimDriverStationData->dsAttached;
bool dsAttached = SimDriverStationData->dsAttached;
std::scoped_lock lock{driverStation->cacheMutex};
JoystickDataCache* prev = currentCache.exchange(nullptr);
if (prev != nullptr) {
currentRead = prev;
}
newestControlWord = controlWord;
// If newest state shows we have a DS attached, just use the
// control word out of the cache, As it will be the one in sync
// with the data. If no data has been updated, at this point,
// and a DS wasn't attached previously, this will still return
// a zeroed out control word, with is the correct state for
// no new data.
if (!dsAttached) {
// If the DS is not attached, we need to zero out the control word.
// This is because HAL_RefreshDSData is called asynchronously from
// the DS data. The dsAttached variable comes directly from netcomm
// and could be updated before the caches are. If that happens,
// we would end up returning the previous cached control word,
// which is out of sync with the current control word and could
// break invariants such as which alliance station is in used.
// Also, when the DS has never been connected the rest of the fields
// in control word are garbage, so we also need to zero out in that
// case too
std::memset(&currentRead->controlWord, 0, sizeof(currentRead->controlWord));
}
newestControlWord = currentRead->controlWord;
return prev != nullptr;
}
@@ -369,6 +392,7 @@ void NewDriverStationData() {
if (gShutdown) {
return;
}
SimDriverStationData->dsAttached = true;
cacheToUpdate->Update();
JoystickDataCache* given = cacheToUpdate;
@@ -382,7 +406,6 @@ void NewDriverStationData() {
}
lastGiven = given;
SimDriverStationData->dsAttached = true;
driverStation->newDataEvents.Wakeup();
SimDriverStationData->CallNewDataCallbacks();
}

View File

@@ -15,7 +15,8 @@ public class PubSubOption {
disableRemote,
disableLocal,
excludePublisher,
excludeSelf
excludeSelf,
hidden
}
PubSubOption(Kind kind, boolean value) {
@@ -149,6 +150,18 @@ public class PubSubOption {
return new PubSubOption(Kind.excludeSelf, enabled);
}
/**
* For subscriptions, don't share the existence of the subscription with the network. Note this
* means updates will not be received from the network unless another subscription overlaps with
* this one, and the subscription will not appear in metatopics.
*
* @param enabled True to enable, false to disable
* @return option
*/
public static PubSubOption hidden(boolean enabled) {
return new PubSubOption(Kind.hidden, enabled);
}
final Kind m_kind;
final boolean m_bValue;
final int m_iValue;

View File

@@ -42,6 +42,9 @@ public class PubSubOptions {
case excludeSelf:
excludeSelf = option.m_bValue;
break;
case hidden:
hidden = option.m_bValue;
break;
default:
break;
}
@@ -58,7 +61,8 @@ public class PubSubOptions {
boolean prefixMatch,
boolean disableRemote,
boolean disableLocal,
boolean excludeSelf) {
boolean excludeSelf,
boolean hidden) {
this.pollStorage = pollStorage;
this.periodic = periodic;
this.excludePublisher = excludePublisher;
@@ -69,6 +73,7 @@ public class PubSubOptions {
this.disableRemote = disableRemote;
this.disableLocal = disableLocal;
this.excludeSelf = excludeSelf;
this.hidden = hidden;
}
/** Default value of periodic. */
@@ -123,4 +128,11 @@ public class PubSubOptions {
/** For entries, don't queue (for readQueue) value updates for the entry's internal publisher. */
public boolean excludeSelf;
/**
* For subscriptions, don't share the existence of the subscription with the network. Note this
* means updates will not be received from the network unless another subscription overlaps with
* this one, and the subscription will not appear in metatopics.
*/
public boolean hidden;
}

View File

@@ -611,7 +611,7 @@ LocalStorage::SubscriberData* LocalStorage::Impl::AddLocalSubscriber(
"published as '{}')",
topic->name, config.typeStr, topic->typeStr);
}
if (m_network) {
if (m_network && !subscriber->config.hidden) {
DEBUG4("-> NetworkSubscribe({})", topic->name);
m_network->Subscribe(subscriber->handle, {{topic->name}}, config);
}
@@ -640,7 +640,7 @@ LocalStorage::Impl::RemoveLocalSubscriber(NT_Subscriber subHandle) {
listener.getSecond()->subscriber = nullptr;
}
}
if (m_network) {
if (m_network && !subscriber->config.hidden) {
m_network->Unsubscribe(subscriber->handle);
}
}
@@ -676,7 +676,7 @@ LocalStorage::MultiSubscriberData* LocalStorage::Impl::AddMultiSubscriber(
}
}
}
if (m_network) {
if (m_network && !subscriber->options.hidden) {
DEBUG4("-> NetworkSubscribe");
m_network->Subscribe(subscriber->handle, subscriber->prefixes,
subscriber->options);
@@ -696,7 +696,7 @@ LocalStorage::Impl::RemoveMultiSubscriber(NT_MultiSubscriber subHandle) {
listener.getSecond()->multiSubscriber = nullptr;
}
}
if (m_network) {
if (m_network && !subscriber->options.hidden) {
m_network->Unsubscribe(subscriber->handle);
}
}
@@ -1128,12 +1128,16 @@ void LocalStorage::Impl::StartNetwork(net::NetworkInterface* network) {
}
}
for (auto&& subscriber : m_subscribers) {
network->Subscribe(subscriber->handle, {{subscriber->topic->name}},
subscriber->config);
if (!subscriber->config.hidden) {
network->Subscribe(subscriber->handle, {{subscriber->topic->name}},
subscriber->config);
}
}
for (auto&& subscriber : m_multiSubscribers) {
network->Subscribe(subscriber->handle, subscriber->prefixes,
subscriber->options);
if (!subscriber->options.hidden) {
network->Subscribe(subscriber->handle, subscriber->prefixes,
subscriber->options);
}
}
}

View File

@@ -139,6 +139,7 @@ static nt::PubSubOptions FromJavaPubSubOptions(JNIEnv* env, jobject joptions) {
FIELD(disableRemote, "Z");
FIELD(disableLocal, "Z");
FIELD(excludeSelf, "Z");
FIELD(hidden, "Z");
#undef FIELD
@@ -154,7 +155,8 @@ static nt::PubSubOptions FromJavaPubSubOptions(JNIEnv* env, jobject joptions) {
FIELD(bool, Boolean, prefixMatch),
FIELD(bool, Boolean, disableRemote),
FIELD(bool, Boolean, disableLocal),
FIELD(bool, Boolean, excludeSelf)};
FIELD(bool, Boolean, excludeSelf),
FIELD(bool, Boolean, hidden)};
#undef GET
#undef FIELD

View File

@@ -1816,7 +1816,8 @@ void ServerImpl::SetValue(ClientData* client, TopicData* topic,
}
for (auto&& tcd : topic->clients) {
if (tcd.second.sendMode != ValueSendMode::kDisabled) {
if (tcd.first != client &&
tcd.second.sendMode != ValueSendMode::kDisabled) {
tcd.first->SendValue(topic, value, tcd.second.sendMode);
}
}

View File

@@ -126,6 +126,7 @@ static PubSubOptions ConvertToCpp(const NT_PubSubOptions* in) {
out.disableRemote = in->disableRemote;
out.disableLocal = in->disableLocal;
out.excludeSelf = in->excludeSelf;
out.hidden = in->hidden;
return out;
}

View File

@@ -312,6 +312,9 @@ class StructArrayPublisher : public Publisher {
void Set(std::span<const T> value, int64_t time = 0) {
std::apply(
[&](const I&... info) {
if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
GetTopic().GetInstance().template AddStructSchema<T>(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<const T> value) {
std::apply(
[&](const I&... info) {
if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
GetTopic().GetInstance().template AddStructSchema<T>(info...);
}
m_buf.Write(
value,
[&](auto bytes) { ::nt::SetDefaultRaw(m_pubHandle, bytes); },

View File

@@ -367,6 +367,14 @@ struct NT_PubSubOptions {
* internal publisher.
*/
NT_Bool excludeSelf;
/**
* For subscriptions, don't share the existence of the subscription with the
* network. Note this means updates will not be received from the network
* unless another subscription overlaps with this one, and the subscription
* will not appear in metatopics.
*/
NT_Bool hidden;
};
/**

View File

@@ -374,6 +374,14 @@ struct PubSubOptions {
* internal publisher.
*/
bool excludeSelf = false;
/**
* For subscriptions, don't share the existence of the subscription with the
* network. Note this means updates will not be received from the network
* unless another subscription overlaps with this one, and the subscription
* will not appear in metatopics.
*/
bool hidden = false;
};
/**

View File

@@ -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

View File

@@ -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

View File

@@ -51,6 +51,8 @@ __declspec(dllexport)
glass::SetStorageName("simgui");
gui::AddInit([] { ImGui::GetIO().ConfigDockingWithShift = true; });
HAL_RegisterExtension(HALSIMGUI_EXT_ADDGUIINIT,
reinterpret_cast<void*>((AddGuiInitFn)&AddGuiInit));
HAL_RegisterExtension(

View File

@@ -4,9 +4,10 @@
#include "sysid/analysis/FeedbackAnalysis.h"
#include <cmath>
#include <frc/controller/LinearQuadraticRegulator.h>
#include <frc/system/LinearSystem.h>
#include <frc/system/plant/LinearSystemId.h>
#include <units/acceleration.h>
#include <units/velocity.h>
#include <units/voltage.h>
@@ -21,22 +22,30 @@ using Ka_t = decltype(1_V / 1_mps_sq);
FeedbackGains sysid::CalculatePositionFeedbackGains(
const FeedbackControllerPreset& preset, const LQRParameters& params,
double Kv, double Ka) {
if (!std::isfinite(Kv) || !std::isfinite(Ka)) {
return {0.0, 0.0};
}
// 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.
if (Ka > 1E-7) {
// Create a position system from our feedforward gains.
auto system = frc::LinearSystemId::IdentifyPositionSystem<units::meter>(
Kv_t(Kv), Ka_t(Ka));
frc::LinearSystem<2, 1, 1> system{
frc::Matrixd<2, 2>{{0.0, 1.0}, {0.0, -Kv / Ka}},
frc::Matrixd<2, 1>{0.0, 1.0 / Ka}, frc::Matrixd<1, 2>{1.0, 0.0},
frc::Matrixd<1, 1>{0.0}};
// Create an LQR with 2 states to control -- position and velocity.
frc::LinearQuadraticRegulator<2, 1> controller{
system, {params.qp, params.qv}, {params.r}, preset.period};
// Compensate for any latency from sensor measurements, filtering, etc.
controller.LatencyCompensate(system, preset.period, 0.0_s);
controller.LatencyCompensate(system, preset.period,
preset.measurementDelay);
return {controller.K(0, 0) * preset.outputConversionFactor,
controller.K(0, 1) * preset.outputConversionFactor /
(preset.normalized ? 1 : preset.period.value())};
return {
controller.K(0, 0) * preset.outputConversionFactor,
controller.K(0, 1) * preset.outputConversionFactor /
(preset.normalized ? 1 : units::second_t{preset.period}.value())};
}
// This is our special model to avoid instabilities in the LQR.
@@ -47,7 +56,7 @@ FeedbackGains sysid::CalculatePositionFeedbackGains(
frc::LinearQuadraticRegulator<1, 1> controller{
system, {params.qp}, {params.r}, preset.period};
// Compensate for any latency from sensor measurements, filtering, etc.
controller.LatencyCompensate(system, preset.period, 0.0_s);
controller.LatencyCompensate(system, preset.period, preset.measurementDelay);
return {Kv * controller.K(0, 0) * preset.outputConversionFactor, 0.0};
}
@@ -55,6 +64,10 @@ FeedbackGains sysid::CalculatePositionFeedbackGains(
FeedbackGains sysid::CalculateVelocityFeedbackGains(
const FeedbackControllerPreset& preset, const LQRParameters& params,
double Kv, double Ka, double encFactor) {
if (!std::isfinite(Kv) || !std::isfinite(Ka)) {
return {0.0, 0.0};
}
// If acceleration for velocity control requires no effort, the feedback
// control gains approach zero. We special-case it here because numerical
// instabilities arise in LQR otherwise.
@@ -63,8 +76,9 @@ FeedbackGains sysid::CalculateVelocityFeedbackGains(
}
// Create a velocity system from our feedforward gains.
auto system = frc::LinearSystemId::IdentifyVelocitySystem<units::meter>(
Kv_t(Kv), Ka_t(Ka));
frc::LinearSystem<1, 1, 1> system{
frc::Matrixd<1, 1>{-Kv / Ka}, frc::Matrixd<1, 1>{1.0 / Ka},
frc::Matrixd<1, 1>{1.0}, frc::Matrixd<1, 1>{0.0}};
// Create an LQR controller with 1 state -- velocity.
frc::LinearQuadraticRegulator<1, 1> controller{
system, {params.qv}, {params.r}, preset.period};

View File

@@ -146,9 +146,18 @@ sysid::TrimStepVoltageData(std::vector<PreparedData>* data,
wpi::sgn(b.velocity) * b.acceleration;
});
// Current limiting can delay onset of the peak acceleration, so we need to
// find the first acceleration *near* the max. Magic number tolerance here
// because this whole file is tech debt already
auto accelBegins = std::find_if(
data->begin(), data->end(), [&maxAccel](const auto& measurement) {
return wpi::sgn(measurement.velocity) * measurement.acceleration >
0.8 * wpi::sgn(maxAccel->velocity) * maxAccel->acceleration;
});
units::second_t velocityDelay;
if (maxAccel != data->end()) {
velocityDelay = maxAccel->timestamp - firstTimestamp;
if (accelBegins != data->end()) {
velocityDelay = accelBegins->timestamp - firstTimestamp;
// Trim data before max acceleration
data->erase(data->begin(), maxAccel);

View File

@@ -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

View File

@@ -33,7 +33,7 @@ class ArmSim {
* forward dt seconds.
*
* @param voltage Voltage to apply over the timestep.
* @param dt Sammple period.
* @param dt Sample period.
*/
void Update(units::volt_t voltage, units::second_t dt);

View File

@@ -31,7 +31,7 @@ class ElevatorSim {
* dt seconds.
*
* @param voltage Voltage to apply over the timestep.
* @param dt Sammple period.
* @param dt Sample period.
*/
void Update(units::volt_t voltage, units::second_t dt);

View File

@@ -30,7 +30,7 @@ class SimpleMotorSim {
* seconds.
*
* @param voltage Voltage to apply over the timestep.
* @param dt Sammple period.
* @param dt Sample period.
*/
void Update(units::volt_t voltage, units::second_t dt);

View File

@@ -103,3 +103,43 @@ TEST(FeedbackAnalysisTest, VelocityREVConversion) {
EXPECT_NEAR(Kp, 0.00241 / 3, 0.005);
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, Position) {
auto Kv = 3.060;
auto Ka = 0.327;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculatePositionFeedbackGains(
sysid::presets::kDefault, params, Kv, Ka);
EXPECT_NEAR(Kp, 6.41, 0.05);
EXPECT_NEAR(Kd, 2.48, 0.05);
}
TEST(FeedbackAnalysisTest, PositionWithLatencyCompensation) {
auto Kv = 3.060;
auto Ka = 0.327;
sysid::LQRParameters params{1, 1.5, 7};
sysid::FeedbackControllerPreset preset{sysid::presets::kDefault};
preset.measurementDelay = 10_ms;
auto [Kp, Kd] = sysid::CalculatePositionFeedbackGains(preset, params, Kv, Ka);
EXPECT_NEAR(Kp, 5.92, 0.05);
EXPECT_NEAR(Kd, 2.12, 0.05);
}
TEST(FeedbackAnalysisTest, PositionREV) {
auto Kv = 3.060;
auto Ka = 0.327;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculatePositionFeedbackGains(
sysid::presets::kREVNEOBuiltIn, params, Kv, Ka);
EXPECT_NEAR(Kp, 0.30202, 0.05);
EXPECT_NEAR(Kd, 48.518, 0.05);
}

View File

@@ -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

View File

@@ -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()

View File

@@ -3,10 +3,9 @@
"version-string": "latest",
"dependencies": [
"opencv",
"eigen3",
"fmt",
"libuv",
"protobuf"
],
"builtin-baseline": "78b61582c9e093fda56a01ebb654be15a0033897"
"builtin-baseline": "37c3e63a1306562f7f59c4c3c8892ddd50fdf992"
}

View File

@@ -63,7 +63,7 @@ public abstract class TrapezoidProfileSubsystem extends SubsystemBase {
@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);
}

View File

@@ -226,7 +226,7 @@ public class SysIdRoutine extends SysIdRoutineLog {
Timer timer = new Timer();
return m_mechanism
.m_subsystem
.runOnce(timer::start)
.runOnce(timer::restart)
.andThen(
m_mechanism.m_subsystem.run(
() -> {

View File

@@ -18,11 +18,7 @@ frc2::CommandPtr SysIdRoutine::Quasistatic(Direction direction) {
double outputSign = direction == Direction::kForward ? 1.0 : -1.0;
return m_mechanism.m_subsystem
->RunOnce([this] {
timer.Reset();
timer.Start();
})
return m_mechanism.m_subsystem->RunOnce([this] { timer.Restart(); })
.AndThen(
m_mechanism.m_subsystem
->Run([this, state, outputSign] {

View File

@@ -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 <cmath>
#include <functional>
#include <memory>

View File

@@ -9,7 +9,6 @@
#pragma warning(disable : 4521)
#endif
#include <concepts>
#include <memory>
#include <type_traits>
#include <utility>
@@ -17,7 +16,6 @@
#include <wpi/DecayedDerivedFrom.h>
#include "frc2/command/CommandBase.h"
#include "frc2/command/CommandHelper.h"
namespace frc2 {

View File

@@ -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 <cmath>
#include <functional>
#include <memory>
#include <frc/Timer.h>
#include <frc/controller/HolonomicDriveController.h>

View File

@@ -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);
}

View File

@@ -4,9 +4,7 @@
#pragma once
#include <concepts>
#include <functional>
#include <memory>
#include <utility>
#include <frc/event/BooleanEvent.h>

View File

@@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj2.command.button;
import static org.junit.jupiter.api.Assertions.assertTrue;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.simulation.DriverStationSim;
import edu.wpi.first.wpilibj2.command.CommandTestBase;
import org.junit.jupiter.api.Test;
@@ -18,7 +17,7 @@ class RobotModeTriggersTest extends CommandTestBase {
DriverStationSim.setAutonomous(true);
DriverStationSim.setTest(false);
DriverStationSim.setEnabled(true);
DriverStation.refreshData();
DriverStationSim.notifyNewData();
Trigger auto = RobotModeTriggers.autonomous();
assertTrue(auto.getAsBoolean());
}
@@ -29,7 +28,7 @@ class RobotModeTriggersTest extends CommandTestBase {
DriverStationSim.setAutonomous(false);
DriverStationSim.setTest(false);
DriverStationSim.setEnabled(true);
DriverStation.refreshData();
DriverStationSim.notifyNewData();
Trigger teleop = RobotModeTriggers.teleop();
assertTrue(teleop.getAsBoolean());
}
@@ -40,7 +39,7 @@ class RobotModeTriggersTest extends CommandTestBase {
DriverStationSim.setAutonomous(false);
DriverStationSim.setTest(true);
DriverStationSim.setEnabled(true);
DriverStation.refreshData();
DriverStationSim.notifyNewData();
Trigger test = RobotModeTriggers.test();
assertTrue(test.getAsBoolean());
}
@@ -51,7 +50,7 @@ class RobotModeTriggersTest extends CommandTestBase {
DriverStationSim.setAutonomous(false);
DriverStationSim.setTest(false);
DriverStationSim.setEnabled(false);
DriverStation.refreshData();
DriverStationSim.notifyNewData();
Trigger disabled = RobotModeTriggers.disabled();
assertTrue(disabled.getAsBoolean());
}

View File

@@ -18,7 +18,7 @@ TEST(RobotModeTriggersTest, Autonomous) {
DriverStationSim::SetAutonomous(true);
DriverStationSim::SetTest(false);
DriverStationSim::SetEnabled(true);
frc::DriverStation::RefreshData();
DriverStationSim::NotifyNewData();
Trigger autonomous = RobotModeTriggers::Autonomous();
EXPECT_TRUE(autonomous.Get());
}
@@ -28,7 +28,7 @@ TEST(RobotModeTriggersTest, Teleop) {
DriverStationSim::SetAutonomous(false);
DriverStationSim::SetTest(false);
DriverStationSim::SetEnabled(true);
frc::DriverStation::RefreshData();
DriverStationSim::NotifyNewData();
Trigger teleop = RobotModeTriggers::Teleop();
EXPECT_TRUE(teleop.Get());
}
@@ -38,7 +38,7 @@ TEST(RobotModeTriggersTest, Disabled) {
DriverStationSim::SetAutonomous(false);
DriverStationSim::SetTest(false);
DriverStationSim::SetEnabled(false);
frc::DriverStation::RefreshData();
DriverStationSim::NotifyNewData();
Trigger disabled = RobotModeTriggers::Disabled();
EXPECT_TRUE(disabled.Get());
}
@@ -48,7 +48,7 @@ TEST(RobotModeTriggersTest, TestMode) {
DriverStationSim::SetAutonomous(false);
DriverStationSim::SetTest(true);
DriverStationSim::SetEnabled(true);
frc::DriverStation::RefreshData();
DriverStationSim::NotifyNewData();
Trigger test = RobotModeTriggers::Test();
EXPECT_TRUE(test.Get());
}

View File

@@ -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<double>(), &status);
HAL_Pulse(m_handle, pulseLength.value(), &status);
FRC_CheckErrorStatus(status, "Channel {}", m_channel);
}

View File

@@ -44,7 +44,7 @@ Tachometer::~Tachometer() {
units::hertz_t Tachometer::GetFrequency() const {
auto period = GetPeriod();
if (period.to<double>() == 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<double>() == 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<double>()};
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<double>(), &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<double>(); }, nullptr);
"RPS", [&] { return GetRevolutionsPerSecond().value(); }, nullptr);
builder.AddDoubleProperty(
"RPM", [&] { return GetRevolutionsPerMinute().to<double>(); }, nullptr);
"RPM", [&] { return GetRevolutionsPerMinute().value(); }, nullptr);
}

View File

@@ -58,7 +58,10 @@ class PowerDistribution : public wpi::Sendable,
double GetVoltage() const;
/**
* Query the temperature of the PDP/PDH.
* Query the temperature of the PDP.
*
* Not supported on the Rev PDH and returns 0.
*
*
* @return The temperature in degrees Celsius
*/
@@ -80,21 +83,27 @@ class PowerDistribution : public wpi::Sendable,
double GetTotalCurrent() const;
/**
* Query the total power drawn from all monitored PDP/PDH channels.
* Query the total power drawn from all monitored PDP channels.
*
* Not supported on the Rev PDH and returns 0.
*
* @return The total power drawn in Watts
*/
double GetTotalPower() const;
/**
* Query the total energy drawn from the monitored PDP/PDH channels.
* Query the total energy drawn from the monitored PDP channels.
*
* Not supported on the Rev PDH and returns 0.
*
* @return The total energy drawn in Joules
*/
double GetTotalEnergy() const;
/**
* Reset the total energy drawn from the PDP/PDH.
* Reset the total energy drawn from the PDP.
*
* Not supported on the Rev PDH and does nothing.
*
* @see PowerDistribution#GetTotalEnergy
*/

View File

@@ -36,13 +36,12 @@ class SendableChooser : public SendableChooserBase {
template <class U>
static U _unwrap_smart_ptr(const U& value);
template <class U>
static U* _unwrap_smart_ptr(const std::unique_ptr<U>& value);
template <class U>
static std::weak_ptr<U> _unwrap_smart_ptr(const std::shared_ptr<U>& value);
public:
using CopyType = decltype(_unwrap_smart_ptr(m_choices.lookup("")));
SendableChooser() = default;
~SendableChooser() override = default;
SendableChooser(SendableChooser&& rhs) = default;
@@ -71,8 +70,8 @@ class SendableChooser : public SendableChooserBase {
void SetDefaultOption(std::string_view name, T object);
/**
* Returns a copy of the selected option (a raw pointer U* if T =
* std::unique_ptr<U> or a std::weak_ptr<U> if T = std::shared_ptr<U>).
* Returns a copy of the selected option (a std::weak_ptr<U> if T =
* std::shared_ptr<U>).
*
* If there is none selected, it will return the default. If there is none
* selected and no default, then it will return a value-initialized instance.
@@ -81,7 +80,7 @@ class SendableChooser : public SendableChooserBase {
*
* @return The option selected
*/
auto GetSelected() -> decltype(_unwrap_smart_ptr(m_choices[""]));
CopyType GetSelected() const;
/**
* Bind a listener that's called when the selected value changes.

View File

@@ -33,8 +33,7 @@ void SendableChooser<T>::SetDefaultOption(std::string_view name, T object) {
template <class T>
requires std::copy_constructible<T> && std::default_initializable<T>
auto SendableChooser<T>::GetSelected()
-> decltype(_unwrap_smart_ptr(m_choices[""])) {
typename SendableChooser<T>::CopyType SendableChooser<T>::GetSelected() const {
std::string selected = m_defaultChoice;
{
std::scoped_lock lock(m_mutex);
@@ -43,9 +42,9 @@ auto SendableChooser<T>::GetSelected()
}
}
if (selected.empty()) {
return decltype(_unwrap_smart_ptr(m_choices[""])){};
return CopyType{};
} else {
return _unwrap_smart_ptr(m_choices[selected]);
return _unwrap_smart_ptr(m_choices.lookup(selected));
}
}
@@ -121,13 +120,6 @@ U SendableChooser<T>::_unwrap_smart_ptr(const U& value) {
return value;
}
template <class T>
requires std::copy_constructible<T> && std::default_initializable<T>
template <class U>
U* SendableChooser<T>::_unwrap_smart_ptr(const std::unique_ptr<U>& value) {
return value.get();
}
template <class T>
requires std::copy_constructible<T> && std::default_initializable<T>
template <class U>

View File

@@ -38,7 +38,7 @@ class SendableChooserBase : public wpi::Sendable,
std::string m_defaultChoice;
std::string m_selected;
bool m_haveSelected = false;
wpi::mutex m_mutex;
mutable wpi::mutex m_mutex;
int m_instance;
std::string m_previousVal;
static std::atomic_int s_instances;

View File

@@ -194,7 +194,7 @@ class SysIdRoutineLog {
private:
LogEntries m_logEntries;
std::string m_logName;
bool m_stateInitialized;
bool m_stateInitialized = false;
wpi::log::StringLogEntry m_state;
};
} // namespace frc::sysid

View File

@@ -131,7 +131,7 @@ class Drivetrain {
nt::DoubleArrayEntry& m_cameraToObjectEntryRef = m_cameraToObjectEntry;
frc::AprilTagFieldLayout m_aprilTagFieldLayout{
frc::LoadAprilTagLayoutField(frc::AprilTagField::k2022RapidReact)};
frc::AprilTagFieldLayout::LoadField(frc::AprilTagField::k2024Crescendo)};
frc::Pose3d m_objectInField{m_aprilTagFieldLayout.GetTagPose(0).value()};
frc::PWMSparkMax m_leftLeader{1};

View File

@@ -44,7 +44,6 @@ class Elevator : public frc2::PIDSubsystem {
private:
frc::PWMSparkMax m_motor{5};
double m_setpoint = 0;
// Conversion value of potentiometer varies between the real world and
// simulation

View File

@@ -41,7 +41,6 @@ class Wrist : public frc2::PIDSubsystem {
private:
frc::PWMSparkMax m_motor{6};
double m_setpoint = 0;
// Conversion value of potentiometer varies between the real world and
// simulation

View File

@@ -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(),

View File

@@ -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))

View File

@@ -91,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);

View File

@@ -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(

View File

@@ -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();
}

View File

@@ -8,6 +8,7 @@
#include <frc/motorcontrol/PWMSparkMax.h>
#include <frc2/command/CommandPtr.h>
#include <frc2/command/SubsystemBase.h>
#include <frc2/command/button/Trigger.h>
#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};

View File

@@ -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()},

View File

@@ -112,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()},

View File

@@ -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(),

View File

@@ -15,14 +15,28 @@ void SysIdRoutineBot::ConfigureBindings() {
[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));
// Using bumpers as a modifier and combining it with the buttons so that we
// can have both sets of bindings at once
(m_driverController.A() && m_driverController.RightBumper())
.WhileTrue(m_drive.SysIdQuasistatic(frc2::sysid::Direction::kForward));
(m_driverController.B() && m_driverController.RightBumper())
.WhileTrue(m_drive.SysIdQuasistatic(frc2::sysid::Direction::kReverse));
(m_driverController.X() && m_driverController.RightBumper())
.WhileTrue(m_drive.SysIdDynamic(frc2::sysid::Direction::kForward));
(m_driverController.Y() && m_driverController.RightBumper())
.WhileTrue(m_drive.SysIdDynamic(frc2::sysid::Direction::kReverse));
m_shooter.SetDefaultCommand(m_shooter.RunShooterCommand(
[this] { return m_driverController.GetLeftTriggerAxis(); }));
(m_driverController.A() && m_driverController.LeftBumper())
.WhileTrue(m_shooter.SysIdQuasistatic(frc2::sysid::Direction::kForward));
(m_driverController.B() && m_driverController.LeftBumper())
.WhileTrue(m_shooter.SysIdQuasistatic(frc2::sysid::Direction::kReverse));
(m_driverController.X() && m_driverController.LeftBumper())
.WhileTrue(m_shooter.SysIdDynamic(frc2::sysid::Direction::kForward));
(m_driverController.Y() && m_driverController.LeftBumper())
.WhileTrue(m_shooter.SysIdDynamic(frc2::sysid::Direction::kReverse));
}
frc2::CommandPtr SysIdRoutineBot::GetAutonomousCommand() {

View File

@@ -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/Shooter.h"
#include <frc2/command/Commands.h>
#include <units/angle.h>
#include <units/voltage.h>
Shooter::Shooter() {
m_shooterEncoder.SetDistancePerPulse(
constants::shooter::kEncoderDistancePerPulse.value());
}
frc2::CommandPtr Shooter::RunShooterCommand(
std::function<double()> shooterSpeed) {
return frc2::cmd::Run(
[this, shooterSpeed] {
m_shooterMotor.SetVoltage(
units::volt_t{m_shooterFeedback.Calculate(
m_shooterEncoder.GetRate(), shooterSpeed())} +
m_shooterFeedforward.Calculate(
units::turns_per_second_t{shooterSpeed()}));
m_feederMotor.Set(constants::shooter::kFeederSpeed);
},
{this})
.WithName("Set Shooter Speed");
}
frc2::CommandPtr Shooter::SysIdQuasistatic(frc2::sysid::Direction direction) {
return m_sysIdRoutine.Quasistatic(direction);
}
frc2::CommandPtr Shooter::SysIdDynamic(frc2::sysid::Direction direction) {
return m_sysIdRoutine.Dynamic(direction);
}

View File

@@ -8,6 +8,7 @@
#include <numbers>
#include <units/angle.h>
#include <units/angular_acceleration.h>
#include <units/angular_velocity.h>
#include <units/length.h>
#include <units/time.h>
@@ -38,8 +39,13 @@ using kv_unit =
units::inverse<units::turns>>;
using kv_unit_t = units::unit_t<kv_unit>;
using ka_unit =
units::compound_unit<units::volts,
units::inverse<units::turns_per_second_squared>>;
using ka_unit_t = units::unit_t<ka_unit>;
inline constexpr std::array<int, 2> kEncoderPorts = {4, 5};
inline constexpr bool kLeftEncoderReversed = false;
inline constexpr bool kEncoderReversed = false;
inline constexpr int kEncoderCpr = 1024;
inline constexpr units::turn_t kEncoderDistancePerPulse =
units::turn_t{1.0} / static_cast<double>(kEncoderCpr);
@@ -55,6 +61,7 @@ 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 ka_unit_t kA = 0_V * 1_s * 1_s / units::turn_t{1};
inline constexpr double kFeederSpeed = 0.5;
} // namespace shooter

View File

@@ -9,6 +9,7 @@
#include "Constants.h"
#include "subsystems/Drive.h"
#include "subsystems/Shooter.h"
class SysIdRoutineBot {
public:
@@ -21,4 +22,5 @@ class SysIdRoutineBot {
frc2::CommandXboxController m_driverController{
constants::oi::kDriverControllerPort};
Drive m_drive;
Shooter m_shooter;
};

View File

@@ -0,0 +1,54 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <functional>
#include <frc/Encoder.h>
#include <frc/RobotController.h>
#include <frc/controller/PIDController.h>
#include <frc/controller/SimpleMotorFeedforward.h>
#include <frc/motorcontrol/PWMSparkMax.h>
#include <frc2/command/SubsystemBase.h>
#include <frc2/command/sysid/SysIdRoutine.h>
#include "Constants.h"
class Shooter : public frc2::SubsystemBase {
public:
Shooter();
frc2::CommandPtr RunShooterCommand(std::function<double()> shooterSpeed);
frc2::CommandPtr SysIdQuasistatic(frc2::sysid::Direction direction);
frc2::CommandPtr SysIdDynamic(frc2::sysid::Direction direction);
private:
frc::PWMSparkMax m_shooterMotor{constants::shooter::kShooterMotorPort};
frc::PWMSparkMax m_feederMotor{constants::shooter::kFeederMotorPort};
frc::Encoder m_shooterEncoder{constants::shooter::kEncoderPorts[0],
constants::shooter::kEncoderPorts[1],
constants::shooter::kEncoderReversed};
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_shooterMotor.SetVoltage(driveVoltage);
},
[this](frc::sysid::SysIdRoutineLog* log) {
log->Motor("shooter-wheel")
.voltage(m_shooterMotor.Get() *
frc::RobotController::GetBatteryVoltage())
.position(units::turn_t{m_shooterEncoder.GetDistance()})
.velocity(
units::turns_per_second_t{m_shooterEncoder.GetRate()});
},
this}};
frc::PIDController m_shooterFeedback{constants::shooter::kP, 0, 0};
frc::SimpleMotorFeedforward<units::turns> m_shooterFeedforward{
constants::shooter::kS, constants::shooter::kV, constants::shooter::kA};
};

View File

@@ -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 <thread>
#include <frc/simulation/DriverStationSim.h>
#include <frc/simulation/SimHooks.h>
#include <gtest/gtest.h>
#include <units/time.h>
#include "Robot.h"
class MecanumControllerCommandTest : public testing::Test {
Robot m_robot;
std::optional<std::thread> 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();
}

View File

@@ -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 <gtest/gtest.h>
#include <hal/HALBase.h>
/**
* 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;
}

View File

@@ -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 <thread>
#include <frc/simulation/DriverStationSim.h>
#include <frc/simulation/SimHooks.h>
#include <gtest/gtest.h>
#include <units/time.h>
#include "Robot.h"
class MecanumControllerCommandTest : public testing::Test {
Robot m_robot;
std::optional<std::thread> 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();
}

View File

@@ -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 <gtest/gtest.h>
#include <hal/HALBase.h>
/**
* 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;
}

View File

@@ -0,0 +1,69 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <iostream>
#include <thread>
#include <frc/simulation/DriverStationSim.h>
#include <frc/simulation/SimHooks.h>
#include <gtest/gtest.h>
#include <units/time.h>
#include "Robot.h"
class SwerveControllerCommandTest : public testing::Test {
Robot m_robot;
std::optional<std::thread> 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();
}

View File

@@ -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 <gtest/gtest.h>
#include <hal/HALBase.h>
/**
* 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;
}

View File

@@ -131,8 +131,7 @@ public class AddressableLEDBuffer {
* @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 class AddressableLEDBuffer {
* @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));
}
}
}

View File

@@ -93,7 +93,9 @@ public class PowerDistribution implements Sendable, AutoCloseable {
}
/**
* Query the temperature of the PDP/PDH.
* Query the temperature of the PDP.
*
* <p>Not supported on the Rev PDH and returns 0.
*
* @return The temperature in degrees Celsius
*/
@@ -121,7 +123,9 @@ public class PowerDistribution implements Sendable, AutoCloseable {
}
/**
* Query the total power drawn from the monitored channels.
* Query the total power drawn from the monitored channels of the PDP.
*
* <p>Not supported on the Rev PDH and returns 0.
*
* @return the total power in Watts
*/
@@ -130,7 +134,9 @@ public class PowerDistribution implements Sendable, AutoCloseable {
}
/**
* Query the total energy drawn from the monitored channels.
* Query the total energy drawn from the monitored channels of the PDP.
*
* <p>Not supported on the Rev PDH and returns 0.
*
* @return the total energy in Joules
*/
@@ -138,7 +144,11 @@ public class PowerDistribution implements Sendable, AutoCloseable {
return PowerDistributionJNI.getTotalEnergy(m_handle);
}
/** Reset the total energy to 0. */
/**
* Reset the total energy to 0 of the PDP.
*
* <p>Not supported on the Rev PDH and does nothing.
*/
public void resetTotalEnergy() {
PowerDistributionJNI.resetTotalEnergy(m_handle);
}

View File

@@ -453,6 +453,11 @@ public abstract class RobotBase implements AutoCloseable {
runRobot(robotSupplier);
}
// On RIO, this will just terminate rather than shutting down cleanly (it's a no-op in sim).
// It's not worth the risk of hanging on shutdown when we want the code to restart as quickly
// as possible.
HAL.terminate();
HAL.shutdown();
System.exit(0);

View File

@@ -6,6 +6,7 @@ package edu.wpi.first.wpilibj;
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 @@ class AddressableLEDBufferTest {
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"));
}
}

View File

@@ -4,6 +4,7 @@
package edu.wpi.first.wpilibj.examples.differentialdriveposeestimator;
import edu.wpi.first.apriltag.AprilTagFieldLayout;
import edu.wpi.first.apriltag.AprilTagFields;
import edu.wpi.first.math.ComputerVisionUtil;
import edu.wpi.first.math.VecBuilder;
@@ -125,7 +126,8 @@ public class Drivetrain {
m_cameraToObjectEntry = cameraToObjectTopic.getEntry(m_defaultVal);
m_objectInField = AprilTagFields.k2022RapidReact.loadAprilTagLayoutField().getTagPose(0).get();
m_objectInField =
AprilTagFieldLayout.loadField(AprilTagFields.k2024Crescendo).getTagPose(0).get();
SmartDashboard.putData("Field", m_fieldSim);
SmartDashboard.putData("FieldEstimation", m_fieldApproximation);

View File

@@ -102,7 +102,6 @@ public class DriveSubsystem extends SubsystemBase {
* @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);
}

View File

@@ -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(

View File

@@ -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");

Some files were not shown because too many files have changed in this diff Show More