mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01:43 +00:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f310a748c | ||
|
|
b43ec87f57 | ||
|
|
19267bef0c | ||
|
|
84cbd48d84 | ||
|
|
1f35750865 | ||
|
|
8230fc631d | ||
|
|
b879a6f8c6 | ||
|
|
49459d3e45 | ||
|
|
4079eabe9b | ||
|
|
fe5d226a19 | ||
|
|
b7535252c2 | ||
|
|
b61ac6db33 | ||
|
|
7b828ce84f | ||
|
|
08a536291b | ||
|
|
193a10d020 | ||
|
|
7867bbde0e | ||
|
|
fa7c01b598 | ||
|
|
2b81610248 | ||
|
|
a4a369b8da | ||
|
|
d991f6e435 | ||
|
|
a27a047ae8 | ||
|
|
2f96cae31a | ||
|
|
83ef8f9658 | ||
|
|
4054893669 | ||
|
|
f75acd11ce | ||
|
|
8bf67b1b33 | ||
|
|
49bb1358d8 | ||
|
|
9c4c07c0f9 | ||
|
|
1a47cc2e86 | ||
|
|
7cd30cffbc | ||
|
|
92aecab2ef | ||
|
|
8785bba080 | ||
|
|
9e5b7b8040 | ||
|
|
917906530a | ||
|
|
00aa66e4fd | ||
|
|
893320544a | ||
|
|
b95d0e060d | ||
|
|
008232b43c | ||
|
|
522be348f4 | ||
|
|
d48a83dee2 | ||
|
|
504fa22143 | ||
|
|
b2b25bf09f | ||
|
|
ce3dc4eb3b | ||
|
|
1ea48caa7d | ||
|
|
fb101925a7 | ||
|
|
657951f6dd | ||
|
|
a60ca9d71c | ||
|
|
f8a45f1558 | ||
|
|
ecba8b99a8 | ||
|
|
e95e88fdf9 | ||
|
|
371d15dec3 | ||
|
|
cb9b8938af | ||
|
|
3b084ecbe0 | ||
|
|
27ba096ea1 | ||
|
|
42c997a3c4 | ||
|
|
5f1a025f27 | ||
|
|
0ebf79b54c | ||
|
|
a8c465f3fb | ||
|
|
a7b1ab683d | ||
|
|
bd6479dc29 | ||
|
|
5cb0340a8c | ||
|
|
ab0e8c37a7 | ||
|
|
b74ac1c645 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,6 +9,8 @@ simgui-ds.json
|
||||
simgui-window.json
|
||||
simgui.json
|
||||
|
||||
networktables.json
|
||||
|
||||
# Created by the jenkins test script
|
||||
test-reports
|
||||
|
||||
|
||||
@@ -40,8 +40,39 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont
|
||||
WPILib uses modified Google style guides for both C++ and Java, which can be found in the [styleguide repository](https://github.com/wpilibsuite/styleguide). Autoformatters are available for many popular editors at https://github.com/google/styleguide. Running wpiformat is required for all contributions and is enforced by our continuous integration system.
|
||||
While the library should be fully formatted according to the styles, additional elements of the style guide were not followed when the library was initially created. All new code should follow the guidelines. If you are looking for some easy ramp-up tasks, finding areas that don't follow the style guide and fixing them is very welcome.
|
||||
|
||||
### Math documentation
|
||||
|
||||
When writing math expressions in documentation, use https://www.unicodeit.net/ to convert LaTeX to a Unicode equivalent that's easier to read. Not all expressions will translate (e.g., superscripts of superscripts) so focus on making it readable by someone who isn't familiar with LaTeX. If content on multiple lines needs to be aligned in Doxygen/Javadoc comments (e.g., integration/summation limits, matrices packed with square brackets and superscripts for them), put them in @verbatim/@endverbatim blocks in Doxygen or `<pre>` tags in Javadoc so they render with monospace font.
|
||||
|
||||
The LaTeX to Unicode conversions can also be done locally via the unicodeit Python package. To install it, execute:
|
||||
```bash
|
||||
pip install --user unicodeit
|
||||
```
|
||||
|
||||
Here's example usage:
|
||||
```bash
|
||||
$ python -m unicodeit.cli 'x_{k+1} = Ax_k + Bu_k'
|
||||
xₖ₊₁ = Axₖ + Buₖ
|
||||
```
|
||||
|
||||
On Linux, this process can be streamlined further by adding the following Bash function to your .bashrc (requires `wl-clipboard` on Wayland or `xclip` on X11):
|
||||
```bash
|
||||
# Converts LaTeX to Unicode, prints the result, and copies it to the clipboard
|
||||
uc() {
|
||||
if [ $WAYLAND_DISPLAY ]; then
|
||||
python -m unicodeit.cli $@ | tee >(wl-copy -n)
|
||||
else
|
||||
python -m unicodeit.cli $@ | tee >(xclip -sel)
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
Here's example usage:
|
||||
```bash
|
||||
$ uc 'x_{k+1} = Ax_k + Bu_k'
|
||||
xₖ₊₁ = Axₖ + Buₖ
|
||||
```
|
||||
|
||||
## Submitting Changes
|
||||
|
||||
### Pull Request Format
|
||||
|
||||
@@ -40,7 +40,7 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
|
||||
|
||||
## Requirements
|
||||
|
||||
- [JDK 11](https://adoptopenjdk.net/)
|
||||
- [JDK 11](https://adoptium.net/temurin/releases/?version=11)
|
||||
- Note that the JRE is insufficient; the full JDK is required
|
||||
- On Ubuntu, run `sudo apt install openjdk-11-jdk`
|
||||
- On Windows, install the JDK 11 .msi from the link above
|
||||
@@ -48,7 +48,7 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
|
||||
- C++ compiler
|
||||
- On Linux, install GCC 11 or greater
|
||||
- On Windows, install [Visual Studio Community 2022](https://visualstudio.microsoft.com/vs/community/) and select the C++ programming language during installation (Gradle can't use the build tools for Visual Studio)
|
||||
- On macOS, install the Xcode command-line build tools via `xcode-select --install`
|
||||
- On macOS, install the Xcode command-line build tools via `xcode-select --install`. Xcode 13 or later is required.
|
||||
- ARM compiler toolchain
|
||||
- Run `./gradlew installRoboRioToolchain` after cloning this repository
|
||||
- If the WPILib installer was used, this toolchain is already installed
|
||||
|
||||
@@ -189,7 +189,10 @@ public class AprilTagFieldLayout {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a field layout from a resource within a jar file.
|
||||
* Deserializes a field layout from a resource within a internal jar file.
|
||||
*
|
||||
* <p>Users should use {@link AprilTagFields#loadAprilTagLayoutField()} to load official layouts
|
||||
* and {@link #AprilTagFieldLayout(String)} for custom layouts.
|
||||
*
|
||||
* @param resourcePath The absolute path of the resource
|
||||
* @return The deserialized layout
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
package edu.wpi.first.apriltag;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public enum AprilTagFields {
|
||||
k2022RapidReact("2022-rapidreact.json"),
|
||||
k2023ChargedUp("2023-chargedup.json");
|
||||
@@ -18,4 +20,14 @@ public enum AprilTagFields {
|
||||
AprilTagFields(String resourceFile) {
|
||||
m_resourceFile = kBaseResourceDir + resourceFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link AprilTagFieldLayout} from the resource JSON.
|
||||
*
|
||||
* @return AprilTagFieldLayout of the field
|
||||
* @throws IOException If the layout does not exist
|
||||
*/
|
||||
public AprilTagFieldLayout loadAprilTagLayoutField() throws IOException {
|
||||
return AprilTagFieldLayout.loadFromResource(m_resourceFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,415 +1,440 @@
|
||||
{
|
||||
"tags" : [ {
|
||||
"ID" : 0,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : -0.0035306,
|
||||
"y" : 7.578928199999999,
|
||||
"z" : 0.8858503999999999
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 1.0,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.0
|
||||
"tags": [
|
||||
{
|
||||
"ID": 0,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": -0.0035306,
|
||||
"y": 7.578928199999999,
|
||||
"z": 0.8858503999999999
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 1,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 3.2327088,
|
||||
"y": 5.486654,
|
||||
"z": 1.7254728
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 2,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 3.067812,
|
||||
"y": 5.3305202,
|
||||
"z": 1.3762228
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.7071067811865476,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": -0.7071067811865475
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 3,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 0.0039878,
|
||||
"y": 5.058536999999999,
|
||||
"z": 0.80645
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 4,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 0.0039878,
|
||||
"y": 3.5124898,
|
||||
"z": 0.80645
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 5,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 0.12110719999999998,
|
||||
"y": 1.7178274,
|
||||
"z": 0.8906002000000001
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.9196502204050923,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.39273842708457407
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 6,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 0.8733027999999999,
|
||||
"y": 0.9412985999999999,
|
||||
"z": 0.8906002000000001
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.9196502204050923,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.39273842708457407
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 7,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 1.6150844,
|
||||
"y": 0.15725139999999999,
|
||||
"z": 0.8906002000000001
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.9196502204050923,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.39273842708457407
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 10,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.4627306,
|
||||
"y": 0.6506718,
|
||||
"z": 0.8858503999999999
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766E-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 11,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 13.2350002,
|
||||
"y": 2.743454,
|
||||
"z": 1.7254728
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766E-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 12,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 13.391388000000001,
|
||||
"y": 2.8998418,
|
||||
"z": 1.3762228
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.7071067811865476,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865475
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 13,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.4552122,
|
||||
"y": 3.1755079999999998,
|
||||
"z": 0.80645
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766E-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 14,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.4552122,
|
||||
"y": 4.7171356,
|
||||
"z": 0.80645
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766E-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 15,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.3350194,
|
||||
"y": 6.5149729999999995,
|
||||
"z": 0.8937752
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.37298778257580906,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.9278362538989199
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 16,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 15.5904946,
|
||||
"y": 7.292695599999999,
|
||||
"z": 0.8906002000000001
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.37298778257580906,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.9278362538989199
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 17,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 14.847188999999998,
|
||||
"y": 8.0691228,
|
||||
"z": 0.8906002000000001
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.37298778257580906,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.9278362538989199
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 40,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 7.874127,
|
||||
"y": 4.9131728,
|
||||
"z": 0.7032752
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.5446390350150271,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.838670567945424
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 41,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 7.4312271999999995,
|
||||
"y": 3.759327,
|
||||
"z": 0.7032752
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.20791169081775934,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.9781476007338057
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 42,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 8.585073,
|
||||
"y": 3.3164272,
|
||||
"z": 0.7032752
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.838670567945424,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": -0.5446390350150271
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 43,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 9.0279728,
|
||||
"y": 4.470273,
|
||||
"z": 0.7032752
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.9781476007338057,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.20791169081775934
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 50,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 7.6790296,
|
||||
"y": 4.3261534,
|
||||
"z": 2.4177244
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.17729273396782605,
|
||||
"X": -0.22744989571511945,
|
||||
"Y": 0.04215534644161733,
|
||||
"Z": 0.9565859910053995
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 51,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 8.0182466,
|
||||
"y": 3.5642296,
|
||||
"z": 2.4177244
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.5510435465842192,
|
||||
"X": -0.19063969497246985,
|
||||
"Y": -0.13102303230819815,
|
||||
"Z": 0.8017733354717242
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 52,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 8.7801704,
|
||||
"y": 3.9034466,
|
||||
"z": 2.4177244
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.9565859910053994,
|
||||
"X": -0.04215534644161739,
|
||||
"Y": -0.22744989571511942,
|
||||
"Z": 0.17729273396782633
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 53,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 8.4409534,
|
||||
"y": 4.6653704,
|
||||
"z": 2.4177244
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.8017733354717241,
|
||||
"X": -0.1310230323081982,
|
||||
"Y": 0.19063969497246983,
|
||||
"Z": 0.5510435465842194
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 1,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 3.2327088,
|
||||
"y" : 5.486654,
|
||||
"z" : 1.7254728
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 1.0,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 2,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 3.067812,
|
||||
"y" : 5.3305202,
|
||||
"z" : 1.3762228
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 0.7071067811865476,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : -0.7071067811865475
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 3,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 0.0039878,
|
||||
"y" : 5.058536999999999,
|
||||
"z" : 0.80645
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 1.0,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 4,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 0.0039878,
|
||||
"y" : 3.5124898,
|
||||
"z" : 0.80645
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 1.0,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 5,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 0.12110719999999998,
|
||||
"y" : 1.7178274,
|
||||
"z" : 0.8906002000000001
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 0.9196502204050923,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.39273842708457407
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 6,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 0.8733027999999999,
|
||||
"y" : 0.9412985999999999,
|
||||
"z" : 0.8906002000000001
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 0.9196502204050923,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.39273842708457407
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 7,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 1.6150844,
|
||||
"y" : 0.15725139999999999,
|
||||
"z" : 0.8906002000000001
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 0.9196502204050923,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.39273842708457407
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 10,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 16.4627306,
|
||||
"y" : 0.6506718,
|
||||
"z" : 0.8858503999999999
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 6.123233995736766E-17,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 11,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 13.2350002,
|
||||
"y" : 2.743454,
|
||||
"z" : 1.7254728
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 6.123233995736766E-17,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 12,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 13.391388000000001,
|
||||
"y" : 2.8998418,
|
||||
"z" : 1.3762228
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 0.7071067811865476,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.7071067811865475
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 13,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 16.4552122,
|
||||
"y" : 3.1755079999999998,
|
||||
"z" : 0.80645
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 6.123233995736766E-17,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 14,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 16.4552122,
|
||||
"y" : 4.7171356,
|
||||
"z" : 0.80645
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 6.123233995736766E-17,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 15,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 16.3350194,
|
||||
"y" : 6.5149729999999995,
|
||||
"z" : 0.8937752
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : -0.37298778257580906,
|
||||
"X" : -0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.9278362538989199
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 16,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 15.5904946,
|
||||
"y" : 7.292695599999999,
|
||||
"z" : 0.8906002000000001
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : -0.37298778257580906,
|
||||
"X" : -0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.9278362538989199
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 17,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 14.847188999999998,
|
||||
"y" : 8.0691228,
|
||||
"z" : 0.8906002000000001
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : -0.37298778257580906,
|
||||
"X" : -0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.9278362538989199
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 40,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 7.874127,
|
||||
"y" : 4.9131728,
|
||||
"z" : 0.7032752
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 0.5446390350150271,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.838670567945424
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 41,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 7.4312271999999995,
|
||||
"y" : 3.759327,
|
||||
"z" : 0.7032752
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : -0.20791169081775934,
|
||||
"X" : -0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.9781476007338057
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 42,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 8.585073,
|
||||
"y" : 3.3164272,
|
||||
"z" : 0.7032752
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 0.838670567945424,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : -0.5446390350150271
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 43,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 9.0279728,
|
||||
"y" : 4.470273,
|
||||
"z" : 0.7032752
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 0.9781476007338057,
|
||||
"X" : 0.0,
|
||||
"Y" : 0.0,
|
||||
"Z" : 0.20791169081775934
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 50,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 7.6790296,
|
||||
"y" : 4.3261534,
|
||||
"z" : 2.4177244
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 0.17729273396782605,
|
||||
"X" : -0.22744989571511945,
|
||||
"Y" : 0.04215534644161733,
|
||||
"Z" : 0.9565859910053995
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 51,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 8.0182466,
|
||||
"y" : 3.5642296,
|
||||
"z" : 2.4177244
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : -0.5510435465842192,
|
||||
"X" : -0.19063969497246985,
|
||||
"Y" : -0.13102303230819815,
|
||||
"Z" : 0.8017733354717242
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 52,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 8.7801704,
|
||||
"y" : 3.9034466,
|
||||
"z" : 2.4177244
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : -0.9565859910053994,
|
||||
"X" : -0.04215534644161739,
|
||||
"Y" : -0.22744989571511942,
|
||||
"Z" : 0.17729273396782633
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"ID" : 53,
|
||||
"pose" : {
|
||||
"translation" : {
|
||||
"x" : 8.4409534,
|
||||
"y" : 4.6653704,
|
||||
"z" : 2.4177244
|
||||
},
|
||||
"rotation" : {
|
||||
"quaternion" : {
|
||||
"W" : 0.8017733354717241,
|
||||
"X" : -0.1310230323081982,
|
||||
"Y" : 0.19063969497246983,
|
||||
"Z" : 0.5510435465842194
|
||||
}
|
||||
}
|
||||
}
|
||||
} ],
|
||||
"field" : {
|
||||
"length" : 16.4592,
|
||||
"width" : 8.2296
|
||||
],
|
||||
"field": {
|
||||
"length": 16.4592,
|
||||
"width": 8.2296
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,16 +23,13 @@ class LoadConfigTest {
|
||||
@ParameterizedTest
|
||||
@EnumSource(AprilTagFields.class)
|
||||
void testLoad(AprilTagFields field) {
|
||||
AprilTagFieldLayout layout =
|
||||
Assertions.assertDoesNotThrow(
|
||||
() -> AprilTagFieldLayout.loadFromResource(field.m_resourceFile));
|
||||
AprilTagFieldLayout layout = Assertions.assertDoesNotThrow(field::loadAprilTagLayoutField);
|
||||
assertNotNull(layout);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test2022RapidReact() throws IOException {
|
||||
AprilTagFieldLayout layout =
|
||||
AprilTagFieldLayout.loadFromResource(AprilTagFields.k2022RapidReact.m_resourceFile);
|
||||
AprilTagFieldLayout layout = AprilTagFields.k2022RapidReact.loadAprilTagLayoutField();
|
||||
|
||||
// Blue Hangar Truss - Hub
|
||||
Pose3d expectedPose =
|
||||
|
||||
@@ -474,6 +474,7 @@ cs::UsbCamera CameraServer::StartAutomaticCapture() {
|
||||
}
|
||||
|
||||
cs::UsbCamera CameraServer::StartAutomaticCapture(int dev) {
|
||||
::GetInstance();
|
||||
cs::UsbCamera camera{fmt::format("USB Camera {}", dev), dev};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
@@ -483,6 +484,7 @@ cs::UsbCamera CameraServer::StartAutomaticCapture(int dev) {
|
||||
|
||||
cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name,
|
||||
int dev) {
|
||||
::GetInstance();
|
||||
cs::UsbCamera camera{name, dev};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
@@ -492,6 +494,7 @@ cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name,
|
||||
|
||||
cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name,
|
||||
std::string_view path) {
|
||||
::GetInstance();
|
||||
cs::UsbCamera camera{name, path};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
@@ -517,6 +520,7 @@ cs::AxisCamera CameraServer::AddAxisCamera(std::span<const std::string> hosts) {
|
||||
|
||||
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
|
||||
std::string_view host) {
|
||||
::GetInstance();
|
||||
cs::AxisCamera camera{name, host};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
@@ -526,6 +530,7 @@ cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
|
||||
|
||||
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
|
||||
const char* host) {
|
||||
::GetInstance();
|
||||
cs::AxisCamera camera{name, host};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
@@ -535,6 +540,7 @@ cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
|
||||
|
||||
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
|
||||
const std::string& host) {
|
||||
::GetInstance();
|
||||
cs::AxisCamera camera{name, host};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
@@ -544,6 +550,7 @@ cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
|
||||
|
||||
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
|
||||
std::span<const std::string> hosts) {
|
||||
::GetInstance();
|
||||
cs::AxisCamera camera{name, hosts};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
@@ -552,10 +559,11 @@ cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
|
||||
}
|
||||
|
||||
cs::MjpegServer CameraServer::AddSwitchedCamera(std::string_view name) {
|
||||
auto& inst = ::GetInstance();
|
||||
// create a dummy CvSource
|
||||
cs::CvSource source{name, cs::VideoMode::PixelFormat::kMJPEG, 160, 120, 30};
|
||||
cs::MjpegServer server = StartAutomaticCapture(source);
|
||||
::GetInstance().m_fixedSources[server.GetHandle()] = source.GetHandle();
|
||||
inst.m_fixedSources[server.GetHandle()] = source.GetHandle();
|
||||
|
||||
return server;
|
||||
}
|
||||
@@ -632,6 +640,7 @@ cs::CvSink CameraServer::GetVideo(std::string_view name) {
|
||||
|
||||
cs::CvSource CameraServer::PutVideo(std::string_view name, int width,
|
||||
int height) {
|
||||
::GetInstance();
|
||||
cs::CvSource source{name, cs::VideoMode::kMJPEG, width, height, 30};
|
||||
StartAutomaticCapture(source);
|
||||
return source;
|
||||
@@ -648,6 +657,7 @@ cs::MjpegServer CameraServer::AddServer(std::string_view name) {
|
||||
}
|
||||
|
||||
cs::MjpegServer CameraServer::AddServer(std::string_view name, int port) {
|
||||
::GetInstance();
|
||||
cs::MjpegServer server{name, port};
|
||||
AddServer(server);
|
||||
return server;
|
||||
|
||||
@@ -104,6 +104,8 @@ static void DisplayMainMenu() {
|
||||
ImGui::Text("v%s", GetWPILibVersion());
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Save location: %s", glass::GetStorageDir().c_str());
|
||||
ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate,
|
||||
ImGui::GetIO().Framerate);
|
||||
if (ImGui::Button("Close")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
@@ -2,9 +2,18 @@
|
||||
"game": "FIRST Power Up",
|
||||
"field-image": "2018-field.jpg",
|
||||
"field-corners": {
|
||||
"top-left": [125, 20],
|
||||
"bottom-right": [827, 370]
|
||||
"top-left": [
|
||||
125,
|
||||
20
|
||||
],
|
||||
"bottom-right": [
|
||||
827,
|
||||
370
|
||||
]
|
||||
},
|
||||
"field-size": [54, 27],
|
||||
"field-size": [
|
||||
54,
|
||||
27
|
||||
],
|
||||
"field-unit": "feet"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
{
|
||||
"game" : "Destination: Deep Space",
|
||||
"field-image" : "2019-field.jpg",
|
||||
"field-corners": {
|
||||
"top-left" : [217, 40],
|
||||
"bottom-right" : [1372, 615]
|
||||
},
|
||||
"field-size" : [54, 27],
|
||||
"field-unit" : "foot"
|
||||
"game": "Destination: Deep Space",
|
||||
"field-image": "2019-field.jpg",
|
||||
"field-corners": {
|
||||
"top-left": [
|
||||
217,
|
||||
40
|
||||
],
|
||||
"bottom-right": [
|
||||
1372,
|
||||
615
|
||||
]
|
||||
},
|
||||
"field-size": [
|
||||
54,
|
||||
27
|
||||
],
|
||||
"field-unit": "foot"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
{
|
||||
"game" : "Infinite Recharge",
|
||||
"field-image" : "2020-field.png",
|
||||
"field-corners": {
|
||||
"top-left" : [96, 25],
|
||||
"bottom-right" : [1040, 514]
|
||||
},
|
||||
"field-size" : [52.4375, 26.9375],
|
||||
"field-unit" : "foot"
|
||||
}
|
||||
"game": "Infinite Recharge",
|
||||
"field-image": "2020-field.png",
|
||||
"field-corners": {
|
||||
"top-left": [
|
||||
96,
|
||||
25
|
||||
],
|
||||
"bottom-right": [
|
||||
1040,
|
||||
514
|
||||
]
|
||||
},
|
||||
"field-size": [
|
||||
52.4375,
|
||||
26.9375
|
||||
],
|
||||
"field-unit": "foot"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
{
|
||||
"game": "Barrel Racing Path",
|
||||
"field-image": "2021-barrel.png",
|
||||
"field-corners": {
|
||||
"top-left": [20, 20],
|
||||
"bottom-right": [780, 400]
|
||||
},
|
||||
"field-size": [30, 15],
|
||||
"field-unit": "feet"
|
||||
}
|
||||
"game": "Barrel Racing Path",
|
||||
"field-image": "2021-barrel.png",
|
||||
"field-corners": {
|
||||
"top-left": [
|
||||
20,
|
||||
20
|
||||
],
|
||||
"bottom-right": [
|
||||
780,
|
||||
400
|
||||
]
|
||||
},
|
||||
"field-size": [
|
||||
30,
|
||||
15
|
||||
],
|
||||
"field-unit": "feet"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
{
|
||||
"game": "Bounce Path",
|
||||
"field-image": "2021-bounce.png",
|
||||
"field-corners": {
|
||||
"top-left": [20, 20],
|
||||
"bottom-right": [780, 400]
|
||||
},
|
||||
"field-size": [30, 15],
|
||||
"field-unit": "feet"
|
||||
}
|
||||
"game": "Bounce Path",
|
||||
"field-image": "2021-bounce.png",
|
||||
"field-corners": {
|
||||
"top-left": [
|
||||
20,
|
||||
20
|
||||
],
|
||||
"bottom-right": [
|
||||
780,
|
||||
400
|
||||
]
|
||||
},
|
||||
"field-size": [
|
||||
30,
|
||||
15
|
||||
],
|
||||
"field-unit": "feet"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
{
|
||||
"game": "Galactic Search A",
|
||||
"field-image": "2021-galacticsearcha.png",
|
||||
"field-corners": {
|
||||
"top-left": [20, 20],
|
||||
"bottom-right": [780, 400]
|
||||
},
|
||||
"field-size": [30, 15],
|
||||
"field-unit": "feet"
|
||||
}
|
||||
"game": "Galactic Search A",
|
||||
"field-image": "2021-galacticsearcha.png",
|
||||
"field-corners": {
|
||||
"top-left": [
|
||||
20,
|
||||
20
|
||||
],
|
||||
"bottom-right": [
|
||||
780,
|
||||
400
|
||||
]
|
||||
},
|
||||
"field-size": [
|
||||
30,
|
||||
15
|
||||
],
|
||||
"field-unit": "feet"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
{
|
||||
"game": "Galactic Search B",
|
||||
"field-image": "2021-galacticsearchb.png",
|
||||
"field-corners": {
|
||||
"top-left": [20, 20],
|
||||
"bottom-right": [780, 400]
|
||||
},
|
||||
"field-size": [30, 15],
|
||||
"field-unit": "feet"
|
||||
}
|
||||
"game": "Galactic Search B",
|
||||
"field-image": "2021-galacticsearchb.png",
|
||||
"field-corners": {
|
||||
"top-left": [
|
||||
20,
|
||||
20
|
||||
],
|
||||
"bottom-right": [
|
||||
780,
|
||||
400
|
||||
]
|
||||
},
|
||||
"field-size": [
|
||||
30,
|
||||
15
|
||||
],
|
||||
"field-unit": "feet"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
{
|
||||
"game": "Infinite Recharge 2021",
|
||||
"field-image": "2021-field.png",
|
||||
"field-corners": {
|
||||
"top-left": [127, 34],
|
||||
"bottom-right": [1323, 649]
|
||||
},
|
||||
"field-size": [52.4375, 26.9375],
|
||||
"field-unit": "foot"
|
||||
}
|
||||
"game": "Infinite Recharge 2021",
|
||||
"field-image": "2021-field.png",
|
||||
"field-corners": {
|
||||
"top-left": [
|
||||
127,
|
||||
34
|
||||
],
|
||||
"bottom-right": [
|
||||
1323,
|
||||
649
|
||||
]
|
||||
},
|
||||
"field-size": [
|
||||
52.4375,
|
||||
26.9375
|
||||
],
|
||||
"field-unit": "foot"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
{
|
||||
"game": "Slalom Path",
|
||||
"field-image": "2021-slalom.png",
|
||||
"field-corners": {
|
||||
"top-left": [20, 20],
|
||||
"bottom-right": [780, 400]
|
||||
},
|
||||
"field-size": [30, 15],
|
||||
"field-unit": "feet"
|
||||
}
|
||||
"game": "Slalom Path",
|
||||
"field-image": "2021-slalom.png",
|
||||
"field-corners": {
|
||||
"top-left": [
|
||||
20,
|
||||
20
|
||||
],
|
||||
"bottom-right": [
|
||||
780,
|
||||
400
|
||||
]
|
||||
},
|
||||
"field-size": [
|
||||
30,
|
||||
15
|
||||
],
|
||||
"field-unit": "feet"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
{
|
||||
"game": "Rapid React",
|
||||
"field-image": "2022-field.png",
|
||||
"field-corners": {
|
||||
"top-left": [74, 50],
|
||||
"bottom-right": [1774, 900]
|
||||
},
|
||||
"field-size": [54, 27],
|
||||
"field-unit": "foot"
|
||||
}
|
||||
"game": "Rapid React",
|
||||
"field-image": "2022-field.png",
|
||||
"field-corners": {
|
||||
"top-left": [
|
||||
74,
|
||||
50
|
||||
],
|
||||
"bottom-right": [
|
||||
1774,
|
||||
900
|
||||
]
|
||||
},
|
||||
"field-size": [
|
||||
54,
|
||||
27
|
||||
],
|
||||
"field-unit": "foot"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
{
|
||||
"game": "Charged Up",
|
||||
"field-image": "2023-field.png",
|
||||
"field-corners": {
|
||||
"top-left": [46, 36],
|
||||
"bottom-right": [1088, 544]
|
||||
},
|
||||
"field-size": [54.27083, 26.2916],
|
||||
"field-unit": "foot"
|
||||
}
|
||||
"game": "Charged Up",
|
||||
"field-image": "2023-field.png",
|
||||
"field-corners": {
|
||||
"top-left": [
|
||||
46,
|
||||
36
|
||||
],
|
||||
"bottom-right": [
|
||||
1088,
|
||||
544
|
||||
]
|
||||
},
|
||||
"field-size": [
|
||||
54.27083,
|
||||
26.2916
|
||||
],
|
||||
"field-unit": "foot"
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ static bool gSetEnterKey = false;
|
||||
static bool gKeyEdit = false;
|
||||
static int* gEnterKey;
|
||||
static void (*gPrevKeyCallback)(GLFWwindow*, int, int, int, int);
|
||||
static bool gNetworkTablesDebugLog = false;
|
||||
|
||||
static void RemapEnterKeyCallback(GLFWwindow* window, int key, int scancode,
|
||||
int action, int mods) {
|
||||
@@ -72,9 +73,8 @@ static void RemapEnterKeyCallback(GLFWwindow* window, int key, int scancode,
|
||||
static void NtInitialize() {
|
||||
auto inst = nt::GetDefaultInstance();
|
||||
auto poller = nt::CreateListenerPoller(inst);
|
||||
nt::AddPolledListener(
|
||||
poller, inst,
|
||||
NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE | NT_EVENT_LOGMESSAGE);
|
||||
nt::AddPolledListener(poller, inst, NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE);
|
||||
nt::AddPolledLogger(poller, 0, 100);
|
||||
gui::AddEarlyExecute([poller] {
|
||||
auto win = gui::GetSystemWindow();
|
||||
if (!win) {
|
||||
@@ -98,6 +98,8 @@ static void NtInitialize() {
|
||||
level = "ERROR: ";
|
||||
} else if (msg->level >= NT_LOG_WARNING) {
|
||||
level = "WARNING: ";
|
||||
} else if (msg->level < NT_LOG_INFO && !gNetworkTablesDebugLog) {
|
||||
continue;
|
||||
}
|
||||
gNetworkTablesLog.Append(fmt::format(
|
||||
"{}{} ({}:{})\n", level, msg->message, msg->filename, msg->line));
|
||||
@@ -232,6 +234,8 @@ int main(int argc, char** argv) {
|
||||
if (gNetworkTablesLogWindow) {
|
||||
gNetworkTablesLogWindow->DisplayMenuItem("NetworkTables Log");
|
||||
}
|
||||
ImGui::MenuItem("NetworkTables Debug Logging", nullptr,
|
||||
&gNetworkTablesDebugLog);
|
||||
ImGui::Separator();
|
||||
gNtProvider->DisplayMenu();
|
||||
ImGui::EndMenu();
|
||||
@@ -265,6 +269,8 @@ int main(int argc, char** argv) {
|
||||
ImGui::Text("v%s", GetWPILibVersion());
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Save location: %s", glass::GetStorageDir().c_str());
|
||||
ImGui::Text("%.3f ms/frame (%.1f FPS)",
|
||||
1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
|
||||
if (ImGui::Button("Close")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ public class DriverStationJNI extends JNIWrapper {
|
||||
|
||||
public static native int sendConsoleLine(String line);
|
||||
|
||||
public static native void refreshDSData();
|
||||
public static native boolean refreshDSData();
|
||||
|
||||
public static native void provideNewDataEventHandle(int handle);
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ struct JoystickDataCache {
|
||||
HAL_JoystickButtons buttons[HAL_kMaxJoysticks];
|
||||
HAL_AllianceStationID allianceStation;
|
||||
float matchTime;
|
||||
HAL_ControlWord controlWord;
|
||||
};
|
||||
static_assert(std::is_standard_layout_v<JoystickDataCache>);
|
||||
// static_assert(std::is_trivial_v<JoystickDataCache>);
|
||||
@@ -104,6 +105,8 @@ void JoystickDataCache::Update() {
|
||||
FRC_NetworkCommunication_getAllianceStation(
|
||||
reinterpret_cast<AllianceStationID_t*>(&allianceStation));
|
||||
FRC_NetworkCommunication_getMatchTime(&matchTime);
|
||||
FRC_NetworkCommunication_getControlWord(
|
||||
reinterpret_cast<ControlWord_t*>(&controlWord));
|
||||
}
|
||||
|
||||
#define CHECK_JOYSTICK_NUMBER(stickNum) \
|
||||
@@ -114,7 +117,7 @@ static HAL_ControlWord newestControlWord;
|
||||
static JoystickDataCache caches[3];
|
||||
static JoystickDataCache* currentRead = &caches[0];
|
||||
static JoystickDataCache* currentReadLocal = &caches[0];
|
||||
static std::atomic<JoystickDataCache*> currentCache{&caches[1]};
|
||||
static std::atomic<JoystickDataCache*> currentCache{nullptr};
|
||||
static JoystickDataCache* lastGiven = &caches[1];
|
||||
static JoystickDataCache* cacheToUpdate = &caches[2];
|
||||
|
||||
@@ -508,17 +511,27 @@ static void newDataOccur(uint32_t refNum) {
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_RefreshDSData(void) {
|
||||
HAL_Bool HAL_RefreshDSData(void) {
|
||||
HAL_ControlWord controlWord;
|
||||
std::memset(&controlWord, 0, sizeof(controlWord));
|
||||
FRC_NetworkCommunication_getControlWord(
|
||||
reinterpret_cast<ControlWord_t*>(&controlWord));
|
||||
std::scoped_lock lock{cacheMutex};
|
||||
JoystickDataCache* prev = currentCache.exchange(nullptr);
|
||||
if (prev != nullptr) {
|
||||
currentRead = prev;
|
||||
JoystickDataCache* prev;
|
||||
{
|
||||
std::scoped_lock lock{cacheMutex};
|
||||
prev = currentCache.exchange(nullptr);
|
||||
if (prev != nullptr) {
|
||||
currentRead = prev;
|
||||
}
|
||||
// If newest state shows we have a DS attached, just use the
|
||||
// control word out of the cache, As it will be the one in sync
|
||||
// with the data. Otherwise use the state that shows disconnected.
|
||||
if (controlWord.dsAttached) {
|
||||
newestControlWord = currentRead->controlWord;
|
||||
} else {
|
||||
newestControlWord = controlWord;
|
||||
}
|
||||
}
|
||||
newestControlWord = controlWord;
|
||||
|
||||
uint32_t mask = tcpMask.exchange(0);
|
||||
if (mask != 0) {
|
||||
@@ -526,6 +539,7 @@ void HAL_RefreshDSData(void) {
|
||||
std::scoped_lock tcpLock(tcpCacheMutex);
|
||||
tcpCache.CloneTo(&tcpCurrent);
|
||||
}
|
||||
return prev != nullptr;
|
||||
}
|
||||
|
||||
void HAL_ProvideNewDataEventHandle(WPI_EventHandle handle) {
|
||||
@@ -551,4 +565,13 @@ void InitializeDriverStation() {
|
||||
setNewDataOccurRef(refNumber);
|
||||
FRC_NetworkCommunication_setNewTcpDataOccurRef(tcpRefNumber);
|
||||
}
|
||||
|
||||
void WaitForInitialPacket() {
|
||||
wpi::Event waitForInitEvent;
|
||||
driverStation->newDataEvents.Add(waitForInitEvent.GetHandle());
|
||||
bool timed_out = false;
|
||||
wpi::WaitForObject(waitForInitEvent.GetHandle(), 0.1, &timed_out);
|
||||
// Don't care what the result is, just want to give it a chance.
|
||||
driverStation->newDataEvents.Remove(waitForInitEvent.GetHandle());
|
||||
}
|
||||
} // namespace hal
|
||||
|
||||
@@ -50,6 +50,7 @@ using namespace hal;
|
||||
|
||||
namespace hal {
|
||||
void InitializeDriverStation();
|
||||
void WaitForInitialPacket();
|
||||
namespace init {
|
||||
void InitializeHAL() {
|
||||
InitializeCTREPCM();
|
||||
@@ -546,6 +547,8 @@ HAL_Bool HAL_Initialize(int32_t timeout, int32_t mode) {
|
||||
return rv;
|
||||
});
|
||||
|
||||
hal::WaitForInitialPacket();
|
||||
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -403,13 +403,13 @@ Java_edu_wpi_first_hal_DriverStationJNI_sendConsoleLine
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_DriverStationJNI
|
||||
* Method: refreshDSData
|
||||
* Signature: ()V
|
||||
* Signature: ()Z
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_edu_wpi_first_hal_DriverStationJNI_refreshDSData
|
||||
(JNIEnv*, jclass)
|
||||
{
|
||||
HAL_RefreshDSData();
|
||||
return HAL_RefreshDSData();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -468,7 +468,11 @@ JNIEXPORT jstring JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_SimDeviceDataJNI_getSimDeviceName
|
||||
(JNIEnv* env, jclass, jint handle)
|
||||
{
|
||||
return MakeJString(env, HALSIM_GetSimDeviceName(handle));
|
||||
const char* name = HALSIM_GetSimDeviceName(handle);
|
||||
if (!name) {
|
||||
return nullptr;
|
||||
}
|
||||
return MakeJString(env, name);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -209,7 +209,7 @@ HAL_Bool HAL_GetOutputsEnabled(void);
|
||||
*/
|
||||
int32_t HAL_GetMatchInfo(HAL_MatchInfo* info);
|
||||
|
||||
void HAL_RefreshDSData(void);
|
||||
HAL_Bool HAL_RefreshDSData(void);
|
||||
|
||||
void HAL_ProvideNewDataEventHandle(WPI_EventHandle handle);
|
||||
void HAL_RemoveNewDataEventHandle(WPI_EventHandle handle);
|
||||
|
||||
@@ -75,7 +75,7 @@ static HAL_ControlWord newestControlWord;
|
||||
static JoystickDataCache caches[3];
|
||||
static JoystickDataCache* currentRead = &caches[0];
|
||||
static JoystickDataCache* currentReadLocal = &caches[0];
|
||||
static std::atomic<JoystickDataCache*> currentCache{&caches[1]};
|
||||
static std::atomic<JoystickDataCache*> currentCache{nullptr};
|
||||
static JoystickDataCache* lastGiven = &caches[1];
|
||||
static JoystickDataCache* cacheToUpdate = &caches[2];
|
||||
|
||||
@@ -318,9 +318,9 @@ void HAL_ObserveUserProgramTest(void) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void HAL_RefreshDSData(void) {
|
||||
HAL_Bool HAL_RefreshDSData(void) {
|
||||
if (gShutdown) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
HAL_ControlWord controlWord;
|
||||
std::memset(&controlWord, 0, sizeof(controlWord));
|
||||
@@ -336,6 +336,7 @@ void HAL_RefreshDSData(void) {
|
||||
currentRead = prev;
|
||||
}
|
||||
newestControlWord = controlWord;
|
||||
return prev != nullptr;
|
||||
}
|
||||
|
||||
void HAL_ProvideNewDataEventHandle(WPI_EventHandle handle) {
|
||||
|
||||
@@ -911,6 +911,14 @@ public final class NetworkTableInstance implements AutoCloseable {
|
||||
NetworkTablesJNI.setServerTeam(m_handle, team, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects the client if it's running and connected. This will automatically start
|
||||
* reconnection attempts to the current server list.
|
||||
*/
|
||||
public void disconnect() {
|
||||
NetworkTablesJNI.disconnect(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts requesting server address from Driver Station. This connects to the Driver Station
|
||||
* running on localhost to obtain the server IP address, and connects with the default port.
|
||||
|
||||
@@ -251,6 +251,8 @@ public final class NetworkTablesJNI {
|
||||
|
||||
public static native void setServerTeam(int inst, int team, int port);
|
||||
|
||||
public static native void disconnect(int inst);
|
||||
|
||||
public static native void startDSClient(int inst, int port);
|
||||
|
||||
public static native void stopDSClient(int inst);
|
||||
|
||||
@@ -1,366 +1,366 @@
|
||||
[
|
||||
{
|
||||
"TypeName": "Boolean",
|
||||
"TypeString": "\"boolean\"",
|
||||
"c": {
|
||||
"ValueType": "NT_Bool",
|
||||
"ParamType": "NT_Bool"
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "bool",
|
||||
"ParamType": "bool",
|
||||
"TYPE_NAME": "BOOLEAN"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "boolean",
|
||||
"EmptyValue": "false",
|
||||
"ConsumerFunctionPackage": "edu.wpi.first.util.function",
|
||||
"FunctionTypePrefix": "Boolean",
|
||||
"ToWrapObject": "Boolean.valueOf",
|
||||
"FromStorageBegin": "(Boolean) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jboolean",
|
||||
"jtypestr": "Z",
|
||||
"JavaObject": false,
|
||||
"FromJavaBegin": "",
|
||||
"FromJavaEnd": " != JNI_FALSE",
|
||||
"ToJavaBegin": "static_cast<jboolean>(",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJBooleanArray"
|
||||
}
|
||||
{
|
||||
"TypeName": "Boolean",
|
||||
"TypeString": "\"boolean\"",
|
||||
"c": {
|
||||
"ValueType": "NT_Bool",
|
||||
"ParamType": "NT_Bool"
|
||||
},
|
||||
{
|
||||
"TypeName": "Integer",
|
||||
"TypeString": "\"int\"",
|
||||
"c": {
|
||||
"ValueType": "int64_t",
|
||||
"ParamType": "int64_t"
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "int64_t",
|
||||
"ParamType": "int64_t",
|
||||
"TYPE_NAME": "INTEGER"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "long",
|
||||
"EmptyValue": "0",
|
||||
"FunctionTypePrefix": "Long",
|
||||
"ToWrapObject": "Long.valueOf",
|
||||
"FromStorageBegin": "((Number) ",
|
||||
"FromStorageEnd": ").longValue()"
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jlong",
|
||||
"jtypestr": "J",
|
||||
"JavaObject": false,
|
||||
"FromJavaBegin": "",
|
||||
"FromJavaEnd": "",
|
||||
"ToJavaBegin": "static_cast<jlong>(",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJLongArray"
|
||||
}
|
||||
"cpp": {
|
||||
"ValueType": "bool",
|
||||
"ParamType": "bool",
|
||||
"TYPE_NAME": "BOOLEAN"
|
||||
},
|
||||
{
|
||||
"TypeName": "Float",
|
||||
"TypeString": "\"float\"",
|
||||
"c": {
|
||||
"ValueType": "float",
|
||||
"ParamType": "float"
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "float",
|
||||
"ParamType": "float",
|
||||
"TYPE_NAME": "FLOAT"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "float",
|
||||
"EmptyValue": "0",
|
||||
"ConsumerFunctionPackage": "edu.wpi.first.util.function",
|
||||
"SupplierFunctionPackage": "edu.wpi.first.util.function",
|
||||
"FunctionTypePrefix": "Float",
|
||||
"ToWrapObject": "Float.valueOf",
|
||||
"FromStorageBegin": "((Number) ",
|
||||
"FromStorageEnd": ").floatValue()"
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jfloat",
|
||||
"jtypestr": "F",
|
||||
"JavaObject": false,
|
||||
"FromJavaBegin": "",
|
||||
"FromJavaEnd": "",
|
||||
"ToJavaBegin": "static_cast<jfloat>(",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJFloatArray"
|
||||
}
|
||||
"java": {
|
||||
"ValueType": "boolean",
|
||||
"EmptyValue": "false",
|
||||
"ConsumerFunctionPackage": "edu.wpi.first.util.function",
|
||||
"FunctionTypePrefix": "Boolean",
|
||||
"ToWrapObject": "Boolean.valueOf",
|
||||
"FromStorageBegin": "(Boolean) "
|
||||
},
|
||||
{
|
||||
"TypeName": "Double",
|
||||
"TypeString": "\"double\"",
|
||||
"c": {
|
||||
"ValueType": "double",
|
||||
"ParamType": "double"
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "double",
|
||||
"ParamType": "double",
|
||||
"TYPE_NAME": "DOUBLE"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "double",
|
||||
"EmptyValue": "0",
|
||||
"FunctionTypePrefix": "Double",
|
||||
"ToWrapObject": "Double.valueOf",
|
||||
"FromStorageBegin": "((Number) ",
|
||||
"FromStorageEnd": ").doubleValue()"
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jdouble",
|
||||
"jtypestr": "D",
|
||||
"JavaObject": false,
|
||||
"FromJavaBegin": "",
|
||||
"FromJavaEnd": "",
|
||||
"ToJavaBegin": "static_cast<jdouble>(",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJDoubleArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "String",
|
||||
"TypeString": "\"string\"",
|
||||
"c": {
|
||||
"ValueType": "char*",
|
||||
"ParamType": "const char*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::string",
|
||||
"ParamType": "std::string_view",
|
||||
"TYPE_NAME": "STRING",
|
||||
"INCLUDES": "#include <string>\n#include <string_view>\n#include <utility>",
|
||||
"SmallRetType": "std::string_view",
|
||||
"SmallElemType": "char"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "String",
|
||||
"EmptyValue": "\"\"",
|
||||
"FunctionTypeSuffix": "<String>",
|
||||
"FromStorageBegin": "(String) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jstring",
|
||||
"jtypestr": "Ljava/lang/String;",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "JStringRef{env, ",
|
||||
"FromJavaEnd": "}",
|
||||
"ToJavaBegin": "MakeJString(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJStringArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "Raw",
|
||||
"c": {
|
||||
"ValueType": "uint8_t*",
|
||||
"ParamType": "const uint8_t*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::vector<uint8_t>",
|
||||
"ParamType": "std::span<const uint8_t>",
|
||||
"DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
|
||||
"TYPE_NAME": "RAW",
|
||||
"INCLUDES": "#include <utility>",
|
||||
"SmallRetType": "std::span<uint8_t>",
|
||||
"SmallElemType": "uint8_t"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "byte[]",
|
||||
"EmptyValue": "new byte[] {}",
|
||||
"FunctionTypeSuffix": "<byte[]>",
|
||||
"FromStorageBegin": "(byte[]) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jbyteArray",
|
||||
"jtypestr": "[B",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "CriticalJByteArrayRef{env, ",
|
||||
"FromJavaEnd": "}.uarray()",
|
||||
"ToJavaBegin": "MakeJByteArray(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJObjectArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "BooleanArray",
|
||||
"TypeString": "\"boolean[]\"",
|
||||
"c": {
|
||||
"ValueType": "NT_Bool*",
|
||||
"ParamType": "const NT_Bool*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::vector<int>",
|
||||
"ParamType": "std::span<const int>",
|
||||
"DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
|
||||
"TYPE_NAME": "BOOLEAN_ARRAY",
|
||||
"INCLUDES": "#include <utility>",
|
||||
"SmallRetType": "std::span<int>",
|
||||
"SmallElemType": "int"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "boolean[]",
|
||||
"WrapValueType": "Boolean[]",
|
||||
"EmptyValue": "new boolean[] {}",
|
||||
"FunctionTypeSuffix": "<boolean[]>",
|
||||
"FromStorageBegin": "(boolean[]) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jbooleanArray",
|
||||
"jtypestr": "[Z",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "FromJavaBooleanArray(env, ",
|
||||
"FromJavaEnd": ")",
|
||||
"ToJavaBegin": "MakeJBooleanArray(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJObjectArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "IntegerArray",
|
||||
"TypeString": "\"int[]\"",
|
||||
"c": {
|
||||
"ValueType": "int64_t*",
|
||||
"ParamType": "const int64_t*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::vector<int64_t>",
|
||||
"ParamType": "std::span<const int64_t>",
|
||||
"DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
|
||||
"TYPE_NAME": "INTEGER_ARRAY",
|
||||
"INCLUDES": "#include <utility>",
|
||||
"SmallRetType": "std::span<int64_t>",
|
||||
"SmallElemType": "int64_t"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "long[]",
|
||||
"WrapValueType": "Long[]",
|
||||
"EmptyValue": "new long[] {}",
|
||||
"FunctionTypeSuffix": "<long[]>",
|
||||
"FromStorageBegin": "(long[]) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jlongArray",
|
||||
"jtypestr": "[J",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "CriticalJLongArrayRef{env, ",
|
||||
"FromJavaEnd": "}",
|
||||
"ToJavaBegin": "MakeJLongArray(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJObjectArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "FloatArray",
|
||||
"TypeString": "\"float[]\"",
|
||||
"c": {
|
||||
"ValueType": "float*",
|
||||
"ParamType": "const float*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::vector<float>",
|
||||
"ParamType": "std::span<const float>",
|
||||
"DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
|
||||
"TYPE_NAME": "FLOAT_ARRAY",
|
||||
"INCLUDES": "#include <utility>",
|
||||
"SmallRetType": "std::span<float>",
|
||||
"SmallElemType": "float"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "float[]",
|
||||
"WrapValueType": "Float[]",
|
||||
"EmptyValue": "new float[] {}",
|
||||
"FunctionTypeSuffix": "<float[]>",
|
||||
"FromStorageBegin": "(float[]) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jfloatArray",
|
||||
"jtypestr": "[F",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "CriticalJFloatArrayRef{env, ",
|
||||
"FromJavaEnd": "}",
|
||||
"ToJavaBegin": "MakeJFloatArray(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJObjectArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "DoubleArray",
|
||||
"TypeString": "\"double[]\"",
|
||||
"c": {
|
||||
"ValueType": "double*",
|
||||
"ParamType": "const double*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::vector<double>",
|
||||
"ParamType": "std::span<const double>",
|
||||
"DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
|
||||
"TYPE_NAME": "DOUBLE_ARRAY",
|
||||
"INCLUDES": "#include <utility>",
|
||||
"SmallRetType": "std::span<double>",
|
||||
"SmallElemType": "double"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "double[]",
|
||||
"WrapValueType": "Double[]",
|
||||
"EmptyValue": "new double[] {}",
|
||||
"FunctionTypeSuffix": "<double[]>",
|
||||
"FromStorageBegin": "(double[]) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jdoubleArray",
|
||||
"jtypestr": "[D",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "CriticalJDoubleArrayRef{env, ",
|
||||
"FromJavaEnd": "}",
|
||||
"ToJavaBegin": "MakeJDoubleArray(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJObjectArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "StringArray",
|
||||
"TypeString": "\"string[]\"",
|
||||
"c": {
|
||||
"ValueType": "struct NT_String*",
|
||||
"ParamType": "const struct NT_String*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::vector<std::string>",
|
||||
"ParamType": "std::span<const std::string>",
|
||||
"DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
|
||||
"TYPE_NAME": "STRING_ARRAY",
|
||||
"INCLUDES": "#include <utility>"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "String[]",
|
||||
"EmptyValue": "new String[] {}",
|
||||
"FunctionTypeSuffix": "<String[]>",
|
||||
"FromStorageBegin": "(String[]) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jobjectArray",
|
||||
"jtypestr": "[Ljava/lang/Object;",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "FromJavaStringArray(env, ",
|
||||
"FromJavaEnd": ")",
|
||||
"ToJavaBegin": "MakeJStringArray(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJObjectArray"
|
||||
}
|
||||
"jni": {
|
||||
"jtype": "jboolean",
|
||||
"jtypestr": "Z",
|
||||
"JavaObject": false,
|
||||
"FromJavaBegin": "",
|
||||
"FromJavaEnd": " != JNI_FALSE",
|
||||
"ToJavaBegin": "static_cast<jboolean>(",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJBooleanArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "Integer",
|
||||
"TypeString": "\"int\"",
|
||||
"c": {
|
||||
"ValueType": "int64_t",
|
||||
"ParamType": "int64_t"
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "int64_t",
|
||||
"ParamType": "int64_t",
|
||||
"TYPE_NAME": "INTEGER"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "long",
|
||||
"EmptyValue": "0",
|
||||
"FunctionTypePrefix": "Long",
|
||||
"ToWrapObject": "Long.valueOf",
|
||||
"FromStorageBegin": "((Number) ",
|
||||
"FromStorageEnd": ").longValue()"
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jlong",
|
||||
"jtypestr": "J",
|
||||
"JavaObject": false,
|
||||
"FromJavaBegin": "",
|
||||
"FromJavaEnd": "",
|
||||
"ToJavaBegin": "static_cast<jlong>(",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJLongArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "Float",
|
||||
"TypeString": "\"float\"",
|
||||
"c": {
|
||||
"ValueType": "float",
|
||||
"ParamType": "float"
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "float",
|
||||
"ParamType": "float",
|
||||
"TYPE_NAME": "FLOAT"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "float",
|
||||
"EmptyValue": "0",
|
||||
"ConsumerFunctionPackage": "edu.wpi.first.util.function",
|
||||
"SupplierFunctionPackage": "edu.wpi.first.util.function",
|
||||
"FunctionTypePrefix": "Float",
|
||||
"ToWrapObject": "Float.valueOf",
|
||||
"FromStorageBegin": "((Number) ",
|
||||
"FromStorageEnd": ").floatValue()"
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jfloat",
|
||||
"jtypestr": "F",
|
||||
"JavaObject": false,
|
||||
"FromJavaBegin": "",
|
||||
"FromJavaEnd": "",
|
||||
"ToJavaBegin": "static_cast<jfloat>(",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJFloatArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "Double",
|
||||
"TypeString": "\"double\"",
|
||||
"c": {
|
||||
"ValueType": "double",
|
||||
"ParamType": "double"
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "double",
|
||||
"ParamType": "double",
|
||||
"TYPE_NAME": "DOUBLE"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "double",
|
||||
"EmptyValue": "0",
|
||||
"FunctionTypePrefix": "Double",
|
||||
"ToWrapObject": "Double.valueOf",
|
||||
"FromStorageBegin": "((Number) ",
|
||||
"FromStorageEnd": ").doubleValue()"
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jdouble",
|
||||
"jtypestr": "D",
|
||||
"JavaObject": false,
|
||||
"FromJavaBegin": "",
|
||||
"FromJavaEnd": "",
|
||||
"ToJavaBegin": "static_cast<jdouble>(",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJDoubleArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "String",
|
||||
"TypeString": "\"string\"",
|
||||
"c": {
|
||||
"ValueType": "char*",
|
||||
"ParamType": "const char*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::string",
|
||||
"ParamType": "std::string_view",
|
||||
"TYPE_NAME": "STRING",
|
||||
"INCLUDES": "#include <string>\n#include <string_view>\n#include <utility>",
|
||||
"SmallRetType": "std::string_view",
|
||||
"SmallElemType": "char"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "String",
|
||||
"EmptyValue": "\"\"",
|
||||
"FunctionTypeSuffix": "<String>",
|
||||
"FromStorageBegin": "(String) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jstring",
|
||||
"jtypestr": "Ljava/lang/String;",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "JStringRef{env, ",
|
||||
"FromJavaEnd": "}",
|
||||
"ToJavaBegin": "MakeJString(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJStringArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "Raw",
|
||||
"c": {
|
||||
"ValueType": "uint8_t*",
|
||||
"ParamType": "const uint8_t*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::vector<uint8_t>",
|
||||
"ParamType": "std::span<const uint8_t>",
|
||||
"DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
|
||||
"TYPE_NAME": "RAW",
|
||||
"INCLUDES": "#include <utility>",
|
||||
"SmallRetType": "std::span<uint8_t>",
|
||||
"SmallElemType": "uint8_t"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "byte[]",
|
||||
"EmptyValue": "new byte[] {}",
|
||||
"FunctionTypeSuffix": "<byte[]>",
|
||||
"FromStorageBegin": "(byte[]) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jbyteArray",
|
||||
"jtypestr": "[B",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "CriticalJByteArrayRef{env, ",
|
||||
"FromJavaEnd": "}.uarray()",
|
||||
"ToJavaBegin": "MakeJByteArray(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJObjectArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "BooleanArray",
|
||||
"TypeString": "\"boolean[]\"",
|
||||
"c": {
|
||||
"ValueType": "NT_Bool*",
|
||||
"ParamType": "const NT_Bool*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::vector<int>",
|
||||
"ParamType": "std::span<const int>",
|
||||
"DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
|
||||
"TYPE_NAME": "BOOLEAN_ARRAY",
|
||||
"INCLUDES": "#include <utility>",
|
||||
"SmallRetType": "std::span<int>",
|
||||
"SmallElemType": "int"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "boolean[]",
|
||||
"WrapValueType": "Boolean[]",
|
||||
"EmptyValue": "new boolean[] {}",
|
||||
"FunctionTypeSuffix": "<boolean[]>",
|
||||
"FromStorageBegin": "(boolean[]) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jbooleanArray",
|
||||
"jtypestr": "[Z",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "FromJavaBooleanArray(env, ",
|
||||
"FromJavaEnd": ")",
|
||||
"ToJavaBegin": "MakeJBooleanArray(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJObjectArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "IntegerArray",
|
||||
"TypeString": "\"int[]\"",
|
||||
"c": {
|
||||
"ValueType": "int64_t*",
|
||||
"ParamType": "const int64_t*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::vector<int64_t>",
|
||||
"ParamType": "std::span<const int64_t>",
|
||||
"DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
|
||||
"TYPE_NAME": "INTEGER_ARRAY",
|
||||
"INCLUDES": "#include <utility>",
|
||||
"SmallRetType": "std::span<int64_t>",
|
||||
"SmallElemType": "int64_t"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "long[]",
|
||||
"WrapValueType": "Long[]",
|
||||
"EmptyValue": "new long[] {}",
|
||||
"FunctionTypeSuffix": "<long[]>",
|
||||
"FromStorageBegin": "(long[]) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jlongArray",
|
||||
"jtypestr": "[J",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "CriticalJLongArrayRef{env, ",
|
||||
"FromJavaEnd": "}",
|
||||
"ToJavaBegin": "MakeJLongArray(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJObjectArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "FloatArray",
|
||||
"TypeString": "\"float[]\"",
|
||||
"c": {
|
||||
"ValueType": "float*",
|
||||
"ParamType": "const float*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::vector<float>",
|
||||
"ParamType": "std::span<const float>",
|
||||
"DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
|
||||
"TYPE_NAME": "FLOAT_ARRAY",
|
||||
"INCLUDES": "#include <utility>",
|
||||
"SmallRetType": "std::span<float>",
|
||||
"SmallElemType": "float"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "float[]",
|
||||
"WrapValueType": "Float[]",
|
||||
"EmptyValue": "new float[] {}",
|
||||
"FunctionTypeSuffix": "<float[]>",
|
||||
"FromStorageBegin": "(float[]) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jfloatArray",
|
||||
"jtypestr": "[F",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "CriticalJFloatArrayRef{env, ",
|
||||
"FromJavaEnd": "}",
|
||||
"ToJavaBegin": "MakeJFloatArray(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJObjectArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "DoubleArray",
|
||||
"TypeString": "\"double[]\"",
|
||||
"c": {
|
||||
"ValueType": "double*",
|
||||
"ParamType": "const double*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::vector<double>",
|
||||
"ParamType": "std::span<const double>",
|
||||
"DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
|
||||
"TYPE_NAME": "DOUBLE_ARRAY",
|
||||
"INCLUDES": "#include <utility>",
|
||||
"SmallRetType": "std::span<double>",
|
||||
"SmallElemType": "double"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "double[]",
|
||||
"WrapValueType": "Double[]",
|
||||
"EmptyValue": "new double[] {}",
|
||||
"FunctionTypeSuffix": "<double[]>",
|
||||
"FromStorageBegin": "(double[]) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jdoubleArray",
|
||||
"jtypestr": "[D",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "CriticalJDoubleArrayRef{env, ",
|
||||
"FromJavaEnd": "}",
|
||||
"ToJavaBegin": "MakeJDoubleArray(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJObjectArray"
|
||||
}
|
||||
},
|
||||
{
|
||||
"TypeName": "StringArray",
|
||||
"TypeString": "\"string[]\"",
|
||||
"c": {
|
||||
"ValueType": "struct NT_String*",
|
||||
"ParamType": "const struct NT_String*",
|
||||
"IsArray": true
|
||||
},
|
||||
"cpp": {
|
||||
"ValueType": "std::vector<std::string>",
|
||||
"ParamType": "std::span<const std::string>",
|
||||
"DefaultValueCopy": "defaultValue.begin(), defaultValue.end()",
|
||||
"TYPE_NAME": "STRING_ARRAY",
|
||||
"INCLUDES": "#include <utility>"
|
||||
},
|
||||
"java": {
|
||||
"ValueType": "String[]",
|
||||
"EmptyValue": "new String[] {}",
|
||||
"FunctionTypeSuffix": "<String[]>",
|
||||
"FromStorageBegin": "(String[]) "
|
||||
},
|
||||
"jni": {
|
||||
"jtype": "jobjectArray",
|
||||
"jtypestr": "[Ljava/lang/Object;",
|
||||
"JavaObject": true,
|
||||
"FromJavaBegin": "FromJavaStringArray(env, ",
|
||||
"FromJavaEnd": ")",
|
||||
"ToJavaBegin": "MakeJStringArray(env, ",
|
||||
"ToJavaEnd": ")",
|
||||
"ToJavaArray": "MakeJObjectArray"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -18,6 +18,7 @@ class INetworkClient {
|
||||
|
||||
virtual void SetServers(
|
||||
std::span<const std::pair<std::string, unsigned int>> servers) = 0;
|
||||
virtual void Disconnect() = 0;
|
||||
|
||||
virtual void StartDSClient(unsigned int port) = 0;
|
||||
virtual void StopDSClient() = 0;
|
||||
|
||||
@@ -501,7 +501,8 @@ bool LSImpl::SetValue(TopicData* topic, const Value& value,
|
||||
if (topic->type != NT_UNASSIGNED && topic->type != value.type()) {
|
||||
return false;
|
||||
}
|
||||
if (!topic->lastValue || value.time() >= topic->lastValue.time()) {
|
||||
if (!topic->lastValue || topic->lastValue.time() == 0 ||
|
||||
value.time() >= topic->lastValue.time()) {
|
||||
// TODO: notify option even if older value
|
||||
topic->type = value.type();
|
||||
topic->lastValue = value;
|
||||
@@ -909,6 +910,7 @@ std::unique_ptr<EntryData> LSImpl::RemoveEntry(NT_Entry entryHandle) {
|
||||
|
||||
MultiSubscriberData* LSImpl::AddMultiSubscriber(
|
||||
std::span<const std::string_view> prefixes, const PubSubOptions& options) {
|
||||
DEBUG4("AddMultiSubscriber({})", fmt::join(prefixes, ","));
|
||||
auto subscriber = m_multiSubscribers.Add(m_inst, prefixes, options);
|
||||
// subscribe to any already existing topics
|
||||
for (auto&& topic : m_topics) {
|
||||
@@ -920,6 +922,7 @@ MultiSubscriberData* LSImpl::AddMultiSubscriber(
|
||||
}
|
||||
}
|
||||
if (m_network) {
|
||||
DEBUG4("-> NetworkSubscribe");
|
||||
m_network->Subscribe(subscriber->handle, subscriber->prefixes,
|
||||
subscriber->options);
|
||||
}
|
||||
@@ -1227,6 +1230,10 @@ PublisherData* LSImpl::PublishEntry(EntryData* entry, NT_Type type) {
|
||||
// create publisher
|
||||
entry->publisher = AddLocalPublisher(entry->topic, wpi::json::object(),
|
||||
entry->subscriber->config);
|
||||
// exclude publisher if requested
|
||||
if (entry->subscriber->config.excludeSelf) {
|
||||
entry->subscriber->config.excludePublisher = entry->publisher->handle;
|
||||
}
|
||||
return entry->publisher;
|
||||
}
|
||||
|
||||
@@ -1279,9 +1286,6 @@ bool LSImpl::SetEntryValue(NT_Handle pubentryHandle, const Value& value) {
|
||||
if (!publisher) {
|
||||
if (auto entry = m_entries.Get(pubentryHandle)) {
|
||||
publisher = PublishEntry(entry, value.type());
|
||||
if (entry->subscriber->config.excludeSelf) {
|
||||
entry->subscriber->config.excludePublisher = publisher->handle;
|
||||
}
|
||||
}
|
||||
if (!publisher) {
|
||||
return false;
|
||||
|
||||
@@ -492,6 +492,11 @@ void NetworkClient::SetServers(
|
||||
m_impl->SetServers(servers, NT_DEFAULT_PORT4);
|
||||
}
|
||||
|
||||
void NetworkClient::Disconnect() {
|
||||
m_impl->m_loopRunner.ExecAsync(
|
||||
[this](auto&) { m_impl->Disconnect("requested by application"); });
|
||||
}
|
||||
|
||||
void NetworkClient::StartDSClient(unsigned int port) {
|
||||
m_impl->StartDSClient(port);
|
||||
}
|
||||
@@ -535,6 +540,11 @@ void NetworkClient3::SetServers(
|
||||
m_impl->SetServers(servers, NT_DEFAULT_PORT3);
|
||||
}
|
||||
|
||||
void NetworkClient3::Disconnect() {
|
||||
m_impl->m_loopRunner.ExecAsync(
|
||||
[this](auto&) { m_impl->Disconnect("requested by application"); });
|
||||
}
|
||||
|
||||
void NetworkClient3::StartDSClient(unsigned int port) {
|
||||
m_impl->StartDSClient(port);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ class NetworkClient final : public INetworkClient {
|
||||
|
||||
void SetServers(
|
||||
std::span<const std::pair<std::string, unsigned int>> servers) final;
|
||||
void Disconnect() final;
|
||||
|
||||
void StartDSClient(unsigned int port) final;
|
||||
void StopDSClient() final;
|
||||
@@ -59,6 +60,7 @@ class NetworkClient3 final : public INetworkClient {
|
||||
|
||||
void SetServers(
|
||||
std::span<const std::pair<std::string, unsigned int>> servers) final;
|
||||
void Disconnect() final;
|
||||
|
||||
void StartDSClient(unsigned int port) final;
|
||||
void StopDSClient() final;
|
||||
|
||||
@@ -360,8 +360,16 @@ void NSImpl::LoadPersistent() {
|
||||
auto size = fs::file_size(m_persistentFilename, ec);
|
||||
wpi::raw_fd_istream is{m_persistentFilename, ec};
|
||||
if (ec.value() != 0) {
|
||||
INFO("could not open persistent file '{}': {}", m_persistentFilename,
|
||||
ec.message());
|
||||
INFO(
|
||||
"could not open persistent file '{}': {} "
|
||||
"(this can be ignored if you aren't expecting persistent values)",
|
||||
m_persistentFilename, ec.message());
|
||||
// try to write an empty file so it doesn't happen again
|
||||
wpi::raw_fd_ostream os{m_persistentFilename, ec, fs::F_Text};
|
||||
if (ec.value() == 0) {
|
||||
os << "[]\n";
|
||||
os.close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
is.readinto(m_persistentData, size);
|
||||
|
||||
@@ -1298,6 +1298,18 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_setServerTeam
|
||||
nt::SetServerTeam(inst, team, port);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: disconnect
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_networktables_NetworkTablesJNI_disconnect
|
||||
(JNIEnv* env, jclass, jint inst)
|
||||
{
|
||||
nt::Disconnect(inst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_networktables_NetworkTablesJNI
|
||||
* Method: startDSClient
|
||||
|
||||
@@ -30,9 +30,9 @@ using namespace nt::net;
|
||||
|
||||
static constexpr uint32_t kMinPeriodMs = 5;
|
||||
|
||||
// maximum number of times the wire can be not ready to send another
|
||||
// maximum amount of time the wire can be not ready to send another
|
||||
// transmission before we close the connection
|
||||
static constexpr int kWireMaxNotReady = 10;
|
||||
static constexpr uint32_t kWireMaxNotReadyMs = 1000;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -58,7 +58,7 @@ class CImpl : public ServerMessageHandler {
|
||||
bool SendControl(uint64_t curTimeMs);
|
||||
void SendValues(uint64_t curTimeMs, bool flush);
|
||||
void SendInitialValues();
|
||||
bool CheckNetworkReady();
|
||||
bool CheckNetworkReady(uint64_t curTimeMs);
|
||||
|
||||
// ServerMessageHandler interface
|
||||
void ServerAnnounce(std::string_view name, int64_t id,
|
||||
@@ -98,7 +98,6 @@ class CImpl : public ServerMessageHandler {
|
||||
// periodic sweep handling
|
||||
uint32_t m_periodMs{kPingIntervalMs + 10};
|
||||
uint64_t m_lastSendMs{0};
|
||||
int m_notReadyCount{0};
|
||||
|
||||
// outgoing queue
|
||||
std::vector<ClientMessage> m_outgoing;
|
||||
@@ -208,7 +207,7 @@ bool CImpl::SendControl(uint64_t curTimeMs) {
|
||||
|
||||
// start a timestamp RTT ping if it's time to do one
|
||||
if (curTimeMs >= m_nextPingTimeMs) {
|
||||
if (!CheckNetworkReady()) {
|
||||
if (!CheckNetworkReady(curTimeMs)) {
|
||||
return false;
|
||||
}
|
||||
auto now = wpi::Now();
|
||||
@@ -219,7 +218,7 @@ bool CImpl::SendControl(uint64_t curTimeMs) {
|
||||
}
|
||||
|
||||
if (!m_outgoing.empty()) {
|
||||
if (!CheckNetworkReady()) {
|
||||
if (!CheckNetworkReady(curTimeMs)) {
|
||||
return false;
|
||||
}
|
||||
auto writer = m_wire.SendText();
|
||||
@@ -258,7 +257,7 @@ void CImpl::SendValues(uint64_t curTimeMs, bool flush) {
|
||||
(flush || curTimeMs >= pub->nextSendMs)) {
|
||||
for (auto&& val : pub->outValues) {
|
||||
if (!checkedNetwork) {
|
||||
if (!CheckNetworkReady()) {
|
||||
if (!CheckNetworkReady(curTimeMs)) {
|
||||
return;
|
||||
}
|
||||
checkedNetwork = true;
|
||||
@@ -268,6 +267,10 @@ void CImpl::SendValues(uint64_t curTimeMs, bool flush) {
|
||||
int64_t time = val.time();
|
||||
if (time != 0) {
|
||||
time += m_serverTimeOffsetUs;
|
||||
// make sure resultant time isn't exactly 0
|
||||
if (time == 0) {
|
||||
time = 1;
|
||||
}
|
||||
}
|
||||
WireEncodeBinary(writer.Add(), Handle{pub->handle}.GetIndex(), time,
|
||||
val);
|
||||
@@ -308,15 +311,13 @@ void CImpl::SendInitialValues() {
|
||||
}
|
||||
}
|
||||
|
||||
bool CImpl::CheckNetworkReady() {
|
||||
bool CImpl::CheckNetworkReady(uint64_t curTimeMs) {
|
||||
if (!m_wire.Ready()) {
|
||||
++m_notReadyCount;
|
||||
if (m_notReadyCount > kWireMaxNotReady) {
|
||||
if (m_lastSendMs != 0 && curTimeMs > (m_lastSendMs + kWireMaxNotReadyMs)) {
|
||||
m_wire.Disconnect("transmit stalled");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
m_notReadyCount = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,9 +48,9 @@ using namespace mpack;
|
||||
|
||||
static constexpr uint32_t kMinPeriodMs = 5;
|
||||
|
||||
// maximum number of times the wire can be not ready to send another
|
||||
// maximum amount of time the wire can be not ready to send another
|
||||
// transmission before we close the connection
|
||||
static constexpr int kWireMaxNotReady = 10;
|
||||
static constexpr uint32_t kWireMaxNotReadyMs = 1000;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -214,7 +214,6 @@ class ClientData4 final : public ClientData4Base {
|
||||
|
||||
private:
|
||||
std::vector<ServerMessage> m_outgoing;
|
||||
int m_notReadyCount{0};
|
||||
|
||||
bool WriteBinary(int64_t id, int64_t time, const Value& value) {
|
||||
return WireEncodeBinary(SendBinary().Add(), id, time, value);
|
||||
@@ -293,7 +292,6 @@ class ClientData3 final : public ClientData, private net3::MessageHandler3 {
|
||||
|
||||
std::vector<net3::Message3> m_outgoing;
|
||||
int64_t m_nextPubUid{1};
|
||||
int m_notReadyCount{0};
|
||||
|
||||
struct TopicData3 {
|
||||
explicit TopicData3(TopicData* topic) { UpdateFlags(topic); }
|
||||
@@ -601,13 +599,17 @@ void ClientData4Base::ClientSetProperties(std::string_view name,
|
||||
auto topicIt = m_server.m_nameTopics.find(name);
|
||||
if (topicIt == m_server.m_nameTopics.end() ||
|
||||
!topicIt->second->IsPublished()) {
|
||||
DEBUG3("ignored SetProperties from {} on non-existent topic '{}'", m_id,
|
||||
name);
|
||||
WARNING(
|
||||
"server ignoring SetProperties({}) from client {} on unpublished topic "
|
||||
"'{}'; publish or set a value first",
|
||||
update.dump(), m_id, name);
|
||||
return; // nothing to do
|
||||
}
|
||||
auto topic = topicIt->second;
|
||||
if (topic->special) {
|
||||
DEBUG3("ignored SetProperties from {} on meta topic '{}'", m_id, name);
|
||||
WARNING(
|
||||
"server ignoring SetProperties({}) from client {} on meta topic '{}'",
|
||||
update.dump(), m_id, name);
|
||||
return; // nothing to do
|
||||
}
|
||||
m_server.SetProperties(nullptr, topic, update);
|
||||
@@ -656,10 +658,13 @@ void ClientData4Base::ClientSubscribe(int64_t subuid,
|
||||
|
||||
// is client already subscribed?
|
||||
bool wasSubscribed = false;
|
||||
bool wasSubscribedValue = false;
|
||||
for (auto subscriber : topic->subscribers) {
|
||||
if (subscriber->client == this) {
|
||||
wasSubscribed = true;
|
||||
break;
|
||||
if (!subscriber->options.topicsOnly) {
|
||||
wasSubscribedValue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,16 +678,17 @@ void ClientData4Base::ClientSubscribe(int64_t subuid,
|
||||
m_server.UpdateMetaTopicSub(topic.get());
|
||||
}
|
||||
|
||||
if (!wasSubscribed && added && !removed) {
|
||||
// announce topic to client
|
||||
// announce topic to client if not previously announced
|
||||
if (added && !removed && !wasSubscribed) {
|
||||
DEBUG4("client {}: announce {}", m_id, topic->name);
|
||||
SendAnnounce(topic.get(), std::nullopt);
|
||||
}
|
||||
|
||||
// send last value
|
||||
if (!sub->options.topicsOnly && topic->lastValue) {
|
||||
DEBUG4("send last value for {} to client {}", topic->name, m_id);
|
||||
SendValue(topic.get(), topic->lastValue, kSendAll);
|
||||
}
|
||||
// send last value
|
||||
if (added && !sub->options.topicsOnly && !wasSubscribedValue &&
|
||||
topic->lastValue) {
|
||||
DEBUG4("send last value for {} to client {}", topic->name, m_id);
|
||||
SendValue(topic.get(), topic->lastValue, kSendAll);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -937,13 +943,11 @@ void ClientData4::SendOutgoing(uint64_t curTimeMs) {
|
||||
}
|
||||
|
||||
if (!m_wire.Ready()) {
|
||||
++m_notReadyCount;
|
||||
if (m_notReadyCount > kWireMaxNotReady) {
|
||||
if (m_lastSendMs != 0 && curTimeMs > (m_lastSendMs + kWireMaxNotReadyMs)) {
|
||||
m_wire.Disconnect("transmit stalled");
|
||||
}
|
||||
return;
|
||||
}
|
||||
m_notReadyCount = 0;
|
||||
|
||||
for (auto&& msg : m_outgoing) {
|
||||
if (auto m = std::get_if<ServerValueMsg>(&msg.contents)) {
|
||||
@@ -1110,13 +1114,11 @@ void ClientData3::SendOutgoing(uint64_t curTimeMs) {
|
||||
}
|
||||
|
||||
if (!m_wire.Ready()) {
|
||||
++m_notReadyCount;
|
||||
if (m_notReadyCount > kWireMaxNotReady) {
|
||||
if (m_lastSendMs != 0 && curTimeMs > (m_lastSendMs + kWireMaxNotReadyMs)) {
|
||||
m_wire.Disconnect("transmit stalled");
|
||||
}
|
||||
return;
|
||||
}
|
||||
m_notReadyCount = 0;
|
||||
|
||||
auto out = m_wire.Send();
|
||||
for (auto&& msg : m_outgoing) {
|
||||
@@ -2125,7 +2127,7 @@ void SImpl::SetFlags(ClientData* client, TopicData* topic, unsigned int flags) {
|
||||
void SImpl::SetValue(ClientData* client, TopicData* topic, const Value& value) {
|
||||
// update retained value if from same client or timestamp newer
|
||||
if (!topic->lastValue || topic->lastValueClient == client ||
|
||||
value.time() >= topic->lastValue.time()) {
|
||||
topic->lastValue.time() == 0 || value.time() >= topic->lastValue.time()) {
|
||||
DEBUG4("updating '{}' last value (time was {} is {})", topic->name,
|
||||
topic->lastValue.time(), value.time());
|
||||
topic->lastValue = value;
|
||||
|
||||
@@ -50,6 +50,10 @@ void WebSocketConnection::Flush() {
|
||||
if (self->m_sendsActive > 0) {
|
||||
--self->m_sendsActive;
|
||||
}
|
||||
} else {
|
||||
for (auto&& buf : bufs) {
|
||||
buf.Deallocate();
|
||||
}
|
||||
}
|
||||
});
|
||||
m_frames.clear();
|
||||
|
||||
@@ -142,10 +142,9 @@ bool nt::net::WireEncodeText(wpi::raw_ostream& os, const ClientMessage& msg) {
|
||||
} else if (auto m = std::get_if<SetPropertiesMsg>(&msg.contents)) {
|
||||
WireEncodeSetProperties(os, m->name, m->update);
|
||||
} else if (auto m = std::get_if<SubscribeMsg>(&msg.contents)) {
|
||||
WireEncodeSubscribe(os, Handle{m->subHandle}.GetIndex(), m->topicNames,
|
||||
m->options);
|
||||
WireEncodeSubscribe(os, m->subHandle, m->topicNames, m->options);
|
||||
} else if (auto m = std::get_if<UnsubscribeMsg>(&msg.contents)) {
|
||||
WireEncodeUnsubscribe(os, Handle{m->subHandle}.GetIndex());
|
||||
WireEncodeUnsubscribe(os, m->subHandle);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ using namespace nt::net3;
|
||||
|
||||
static constexpr uint32_t kMinPeriodMs = 5;
|
||||
|
||||
// maximum number of times the wire can be not ready to send another
|
||||
// maximum amount of time the wire can be not ready to send another
|
||||
// transmission before we close the connection
|
||||
static constexpr int kWireMaxNotReady = 10;
|
||||
static constexpr uint32_t kWireMaxNotReadyMs = 1000;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -93,7 +93,7 @@ class CImpl : public MessageHandler3 {
|
||||
void HandleLocal(std::span<const net::ClientMessage> msgs);
|
||||
void SendPeriodic(uint64_t curTimeMs, bool initial, bool flush);
|
||||
void SendValue(Writer& out, Entry* entry, const Value& value);
|
||||
bool CheckNetworkReady();
|
||||
bool CheckNetworkReady(uint64_t curTimeMs);
|
||||
|
||||
// Outgoing handlers
|
||||
void Publish(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
@@ -142,7 +142,6 @@ class CImpl : public MessageHandler3 {
|
||||
uint32_t m_periodMs{kKeepAliveIntervalMs + 10};
|
||||
uint64_t m_lastSendMs{0};
|
||||
uint64_t m_nextKeepAliveTimeMs;
|
||||
int m_notReadyCount{0};
|
||||
|
||||
// indexed by publisher index
|
||||
std::vector<std::unique_ptr<PublisherData>> m_publishers;
|
||||
@@ -235,7 +234,7 @@ void CImpl::SendPeriodic(uint64_t curTimeMs, bool initial, bool flush) {
|
||||
|
||||
// send keep-alives
|
||||
if (curTimeMs >= m_nextKeepAliveTimeMs) {
|
||||
if (!CheckNetworkReady()) {
|
||||
if (!CheckNetworkReady(curTimeMs)) {
|
||||
return;
|
||||
}
|
||||
DEBUG4("Sending keep alive");
|
||||
@@ -246,7 +245,7 @@ void CImpl::SendPeriodic(uint64_t curTimeMs, bool initial, bool flush) {
|
||||
|
||||
// send any stored-up flags updates
|
||||
if (!m_outgoingFlags.empty()) {
|
||||
if (!CheckNetworkReady()) {
|
||||
if (!CheckNetworkReady(curTimeMs)) {
|
||||
return;
|
||||
}
|
||||
for (auto&& p : m_outgoingFlags) {
|
||||
@@ -261,7 +260,7 @@ void CImpl::SendPeriodic(uint64_t curTimeMs, bool initial, bool flush) {
|
||||
if (pub && !pub->outValues.empty() &&
|
||||
(flush || curTimeMs >= pub->nextSendMs)) {
|
||||
if (!checkedNetwork) {
|
||||
if (!CheckNetworkReady()) {
|
||||
if (!CheckNetworkReady(curTimeMs)) {
|
||||
return;
|
||||
}
|
||||
checkedNetwork = true;
|
||||
@@ -302,15 +301,13 @@ void CImpl::SendValue(Writer& out, Entry* entry, const Value& value) {
|
||||
}
|
||||
}
|
||||
|
||||
bool CImpl::CheckNetworkReady() {
|
||||
bool CImpl::CheckNetworkReady(uint64_t curTimeMs) {
|
||||
if (!m_wire.Ready()) {
|
||||
++m_notReadyCount;
|
||||
if (m_notReadyCount > kWireMaxNotReady) {
|
||||
if (m_lastSendMs != 0 && curTimeMs > (m_lastSendMs + kWireMaxNotReadyMs)) {
|
||||
m_wire.Disconnect("transmit stalled");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
m_notReadyCount = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -537,6 +537,10 @@ void NT_SetServerTeam(NT_Inst inst, unsigned int team, unsigned int port) {
|
||||
nt::SetServerTeam(inst, team, port);
|
||||
}
|
||||
|
||||
void NT_Disconnect(NT_Inst inst) {
|
||||
nt::Disconnect(inst);
|
||||
}
|
||||
|
||||
void NT_StartDSClient(NT_Inst inst, unsigned int port) {
|
||||
nt::StartDSClient(inst, port);
|
||||
}
|
||||
|
||||
@@ -685,6 +685,14 @@ void SetServerTeam(NT_Inst inst, unsigned int team, unsigned int port) {
|
||||
}
|
||||
}
|
||||
|
||||
void Disconnect(NT_Inst inst) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
if (auto client = ii->GetClient()) {
|
||||
client->Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StartDSClient(NT_Inst inst, unsigned int port) {
|
||||
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
|
||||
if (auto client = ii->GetClient()) {
|
||||
|
||||
@@ -586,6 +586,12 @@ class NetworkTableInstance final {
|
||||
*/
|
||||
void SetServerTeam(unsigned int team, unsigned int port = 0);
|
||||
|
||||
/**
|
||||
* Disconnects the client if it's running and connected. This will
|
||||
* automatically start reconnection attempts to the current server list.
|
||||
*/
|
||||
void Disconnect();
|
||||
|
||||
/**
|
||||
* Starts requesting server address from Driver Station.
|
||||
* This connects to the Driver Station running on localhost to obtain the
|
||||
|
||||
@@ -163,6 +163,10 @@ inline void NetworkTableInstance::SetServerTeam(unsigned int team,
|
||||
::nt::SetServerTeam(m_handle, team, port);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::Disconnect() {
|
||||
::nt::Disconnect(m_handle);
|
||||
}
|
||||
|
||||
inline void NetworkTableInstance::StartDSClient(unsigned int port) {
|
||||
::nt::StartDSClient(m_handle, port);
|
||||
}
|
||||
|
||||
@@ -1147,6 +1147,14 @@ void NT_SetServerMulti(NT_Inst inst, size_t count, const char** server_names,
|
||||
*/
|
||||
void NT_SetServerTeam(NT_Inst inst, unsigned int team, unsigned int port);
|
||||
|
||||
/**
|
||||
* Disconnects the client if it's running and connected. This will automatically
|
||||
* start reconnection attempts to the current server list.
|
||||
*
|
||||
* @param inst instance handle
|
||||
*/
|
||||
void NT_Disconnect(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Starts requesting server address from Driver Station.
|
||||
* This connects to the Driver Station running on localhost to obtain the
|
||||
|
||||
@@ -1087,6 +1087,14 @@ void SetServer(
|
||||
*/
|
||||
void SetServerTeam(NT_Inst inst, unsigned int team, unsigned int port);
|
||||
|
||||
/**
|
||||
* Disconnects the client if it's running and connected. This will automatically
|
||||
* start reconnection attempts to the current server list.
|
||||
*
|
||||
* @param inst instance handle
|
||||
*/
|
||||
void Disconnect(NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Starts requesting server address from Driver Station.
|
||||
* This connects to the Driver Station running on localhost to obtain the
|
||||
|
||||
@@ -33,8 +33,10 @@ class SpanMatcher : public ::testing::MatcherInterface<std::span<T>> {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline ::testing::Matcher<std::span<const T>> SpanEq(std::span<const T> good) {
|
||||
return ::testing::MakeMatcher(new SpanMatcher(good));
|
||||
inline ::testing::Matcher<std::span<const typename T::value_type>> SpanEq(
|
||||
const T& good) {
|
||||
return ::testing::MakeMatcher(
|
||||
new SpanMatcher(std::span<const typename T::value_type>(good)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
||||
333
ntcore/src/test/native/cpp/net/ServerImplTest.cpp
Normal file
333
ntcore/src/test/native/cpp/net/ServerImplTest.cpp
Normal file
@@ -0,0 +1,333 @@
|
||||
// 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 <stdint.h>
|
||||
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "../MockLogger.h"
|
||||
#include "../PubSubOptionsMatcher.h"
|
||||
#include "../SpanMatcher.h"
|
||||
#include "../TestPrinters.h"
|
||||
#include "../ValueMatcher.h"
|
||||
#include "Handle.h"
|
||||
#include "MockNetworkInterface.h"
|
||||
#include "MockWireConnection.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "net/Message.h"
|
||||
#include "net/ServerImpl.h"
|
||||
#include "net/WireEncoder.h"
|
||||
#include "ntcore_c.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::AllOf;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Field;
|
||||
using ::testing::IsEmpty;
|
||||
using ::testing::Property;
|
||||
using ::testing::Return;
|
||||
|
||||
using MockSetPeriodicFunc = ::testing::MockFunction<void(uint32_t repeatMs)>;
|
||||
using MockConnected3Func =
|
||||
::testing::MockFunction<void(std::string_view name, uint16_t proto)>;
|
||||
|
||||
namespace nt {
|
||||
|
||||
class ServerImplTest : public ::testing::Test {
|
||||
public:
|
||||
::testing::StrictMock<net::MockLocalInterface> local;
|
||||
wpi::MockLogger logger;
|
||||
net::ServerImpl server{logger};
|
||||
};
|
||||
|
||||
TEST_F(ServerImplTest, AddClient) {
|
||||
::testing::StrictMock<net::MockWireConnection> wire;
|
||||
EXPECT_CALL(wire, Flush());
|
||||
MockSetPeriodicFunc setPeriodic;
|
||||
auto [name, id] = server.AddClient("test", "connInfo", false, wire,
|
||||
setPeriodic.AsStdFunction());
|
||||
EXPECT_EQ(name, "test");
|
||||
EXPECT_NE(id, -1);
|
||||
}
|
||||
|
||||
TEST_F(ServerImplTest, AddDuplicateClient) {
|
||||
::testing::StrictMock<net::MockWireConnection> wire1;
|
||||
::testing::StrictMock<net::MockWireConnection> wire2;
|
||||
MockSetPeriodicFunc setPeriodic1;
|
||||
MockSetPeriodicFunc setPeriodic2;
|
||||
EXPECT_CALL(wire1, Flush());
|
||||
EXPECT_CALL(wire2, Flush());
|
||||
|
||||
auto [name1, id1] = server.AddClient("test", "connInfo", false, wire1,
|
||||
setPeriodic1.AsStdFunction());
|
||||
auto [name2, id2] = server.AddClient("test", "connInfo2", false, wire2,
|
||||
setPeriodic2.AsStdFunction());
|
||||
EXPECT_EQ(name1, "test");
|
||||
EXPECT_NE(id1, -1);
|
||||
EXPECT_EQ(name2, "test@1");
|
||||
EXPECT_NE(id1, id2);
|
||||
EXPECT_NE(id2, -1);
|
||||
}
|
||||
|
||||
TEST_F(ServerImplTest, AddClient3) {}
|
||||
|
||||
template <typename T>
|
||||
static std::string EncodeText(const T& msgs) {
|
||||
std::string data;
|
||||
wpi::raw_string_ostream os{data};
|
||||
bool first = true;
|
||||
for (auto&& msg : msgs) {
|
||||
if (first) {
|
||||
os << '[';
|
||||
first = false;
|
||||
} else {
|
||||
os << ',';
|
||||
}
|
||||
net::WireEncodeText(os, msg);
|
||||
}
|
||||
os << ']';
|
||||
return data;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::vector<uint8_t> EncodeServerBinary(const T& msgs) {
|
||||
std::vector<uint8_t> data;
|
||||
wpi::raw_uvector_ostream os{data};
|
||||
for (auto&& msg : msgs) {
|
||||
if constexpr (std::is_same_v<typename T::value_type, net::ServerMessage>) {
|
||||
if (auto m = std::get_if<net::ServerValueMsg>(&msg.contents)) {
|
||||
net::WireEncodeBinary(os, m->topic, m->value.time(), m->value);
|
||||
}
|
||||
} else if constexpr (std::is_same_v<typename T::value_type,
|
||||
net::ClientMessage>) {
|
||||
if (auto m = std::get_if<net::ClientValueMsg>(&msg.contents)) {
|
||||
net::WireEncodeBinary(os, Handle{m->pubHandle}.GetIndex(),
|
||||
m->value.time(), m->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
TEST_F(ServerImplTest, PublishLocal) {
|
||||
// publish before client connect
|
||||
server.SetLocal(&local);
|
||||
NT_Publisher pubHandle = nt::Handle{0, 1, nt::Handle::kPublisher};
|
||||
NT_Topic topicHandle = nt::Handle{0, 1, nt::Handle::kTopic};
|
||||
NT_Publisher pubHandle2 = nt::Handle{0, 2, nt::Handle::kPublisher};
|
||||
NT_Topic topicHandle2 = nt::Handle{0, 2, nt::Handle::kTopic};
|
||||
NT_Publisher pubHandle3 = nt::Handle{0, 3, nt::Handle::kPublisher};
|
||||
NT_Topic topicHandle3 = nt::Handle{0, 3, nt::Handle::kTopic};
|
||||
{
|
||||
::testing::InSequence seq;
|
||||
EXPECT_CALL(local, NetworkAnnounce("test", "double", wpi::json::object(),
|
||||
pubHandle));
|
||||
EXPECT_CALL(local, NetworkAnnounce("test2", "double", wpi::json::object(),
|
||||
pubHandle2));
|
||||
EXPECT_CALL(local, NetworkAnnounce("test3", "double", wpi::json::object(),
|
||||
pubHandle3));
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<net::ClientMessage> msgs;
|
||||
msgs.emplace_back(net::ClientMessage{net::PublishMsg{
|
||||
pubHandle, topicHandle, "test", "double", wpi::json::object(), {}}});
|
||||
server.HandleLocal(msgs);
|
||||
}
|
||||
|
||||
// client connect; it should get already-published topic as soon as it
|
||||
// subscribes
|
||||
::testing::StrictMock<net::MockWireConnection> wire;
|
||||
MockSetPeriodicFunc setPeriodic;
|
||||
{
|
||||
::testing::InSequence seq;
|
||||
EXPECT_CALL(wire, Flush()); // AddClient()
|
||||
EXPECT_CALL(setPeriodic, Call(100)); // ClientSubscribe()
|
||||
EXPECT_CALL(wire, Flush()); // ClientSubscribe()
|
||||
EXPECT_CALL(wire, Ready()).WillOnce(Return(true)); // SendControl()
|
||||
{
|
||||
std::vector<net::ServerMessage> smsgs;
|
||||
smsgs.emplace_back(net::ServerMessage{net::AnnounceMsg{
|
||||
"test", 3, "double", std::nullopt, wpi::json::object()}});
|
||||
smsgs.emplace_back(net::ServerMessage{net::AnnounceMsg{
|
||||
"test2", 8, "double", std::nullopt, wpi::json::object()}});
|
||||
EXPECT_CALL(wire, Text(EncodeText(smsgs))); // SendControl()
|
||||
}
|
||||
EXPECT_CALL(wire, Flush()); // SendControl()
|
||||
EXPECT_CALL(wire, Ready()).WillOnce(Return(true)); // SendControl()
|
||||
{
|
||||
std::vector<net::ServerMessage> smsgs;
|
||||
smsgs.emplace_back(net::ServerMessage{net::AnnounceMsg{
|
||||
"test3", 11, "double", std::nullopt, wpi::json::object()}});
|
||||
EXPECT_CALL(wire, Text(EncodeText(smsgs))); // SendControl()
|
||||
}
|
||||
EXPECT_CALL(wire, Flush()); // SendControl()
|
||||
}
|
||||
auto [name, id] = server.AddClient("test", "connInfo", false, wire,
|
||||
setPeriodic.AsStdFunction());
|
||||
|
||||
{
|
||||
NT_Subscriber subHandle = nt::Handle{0, 1, nt::Handle::kSubscriber};
|
||||
std::vector<net::ClientMessage> msgs;
|
||||
msgs.emplace_back(net::ClientMessage{net::SubscribeMsg{
|
||||
subHandle, {{""}}, PubSubOptions{.prefixMatch = true}}});
|
||||
server.ProcessIncomingText(id, EncodeText(msgs));
|
||||
}
|
||||
|
||||
// publish before send control
|
||||
{
|
||||
std::vector<net::ClientMessage> msgs;
|
||||
msgs.emplace_back(net::ClientMessage{net::PublishMsg{
|
||||
pubHandle2, topicHandle2, "test2", "double", wpi::json::object(), {}}});
|
||||
server.HandleLocal(msgs);
|
||||
}
|
||||
|
||||
server.SendControl(100);
|
||||
|
||||
// publish after send control
|
||||
{
|
||||
std::vector<net::ClientMessage> msgs;
|
||||
msgs.emplace_back(net::ClientMessage{net::PublishMsg{
|
||||
pubHandle3, topicHandle3, "test3", "double", wpi::json::object(), {}}});
|
||||
server.HandleLocal(msgs);
|
||||
}
|
||||
|
||||
server.SendControl(200);
|
||||
}
|
||||
|
||||
TEST_F(ServerImplTest, ClientSubTopicOnlyThenValue) {
|
||||
// publish before client connect
|
||||
server.SetLocal(&local);
|
||||
NT_Publisher pubHandle = nt::Handle{0, 1, nt::Handle::kPublisher};
|
||||
NT_Topic topicHandle = nt::Handle{0, 1, nt::Handle::kTopic};
|
||||
EXPECT_CALL(
|
||||
local, NetworkAnnounce("test", "double", wpi::json::object(), pubHandle));
|
||||
|
||||
{
|
||||
std::vector<net::ClientMessage> msgs;
|
||||
msgs.emplace_back(net::ClientMessage{net::PublishMsg{
|
||||
pubHandle, topicHandle, "test", "double", wpi::json::object(), {}}});
|
||||
msgs.emplace_back(net::ClientMessage{
|
||||
net::ClientValueMsg{pubHandle, Value::MakeDouble(1.0, 10)}});
|
||||
server.HandleLocal(msgs);
|
||||
}
|
||||
|
||||
::testing::StrictMock<net::MockWireConnection> wire;
|
||||
MockSetPeriodicFunc setPeriodic;
|
||||
{
|
||||
::testing::InSequence seq;
|
||||
EXPECT_CALL(wire, Flush()); // AddClient()
|
||||
EXPECT_CALL(setPeriodic, Call(100)); // ClientSubscribe()
|
||||
EXPECT_CALL(wire, Flush()); // ClientSubscribe()
|
||||
EXPECT_CALL(wire, Ready()).WillOnce(Return(true)); // SendValues()
|
||||
{
|
||||
std::vector<net::ServerMessage> smsgs;
|
||||
smsgs.emplace_back(net::ServerMessage{net::AnnounceMsg{
|
||||
"test", 3, "double", std::nullopt, wpi::json::object()}});
|
||||
EXPECT_CALL(wire, Text(EncodeText(smsgs))); // SendValues()
|
||||
}
|
||||
EXPECT_CALL(wire, Flush()); // SendValues()
|
||||
EXPECT_CALL(setPeriodic, Call(100)); // ClientSubscribe()
|
||||
EXPECT_CALL(wire, Flush()); // ClientSubscribe()
|
||||
EXPECT_CALL(wire, Ready()).WillOnce(Return(true)); // SendValues()
|
||||
{
|
||||
std::vector<net::ServerMessage> smsgs;
|
||||
smsgs.emplace_back(net::ServerMessage{
|
||||
net::ServerValueMsg{3, Value::MakeDouble(1.0, 10)}});
|
||||
EXPECT_CALL(
|
||||
wire,
|
||||
Binary(wpi::SpanEq(EncodeServerBinary(smsgs)))); // SendValues()
|
||||
}
|
||||
EXPECT_CALL(wire, Flush()); // SendValues()
|
||||
}
|
||||
|
||||
// connect client
|
||||
auto [name, id] = server.AddClient("test", "connInfo", false, wire,
|
||||
setPeriodic.AsStdFunction());
|
||||
|
||||
// subscribe topics only; will not send value
|
||||
{
|
||||
NT_Subscriber subHandle = nt::Handle{0, 1, nt::Handle::kSubscriber};
|
||||
std::vector<net::ClientMessage> msgs;
|
||||
msgs.emplace_back(net::ClientMessage{net::SubscribeMsg{
|
||||
subHandle,
|
||||
{{""}},
|
||||
PubSubOptions{.topicsOnly = true, .prefixMatch = true}}});
|
||||
server.ProcessIncomingText(id, EncodeText(msgs));
|
||||
}
|
||||
|
||||
server.SendValues(id, 100);
|
||||
|
||||
// subscribe normal; will not resend announcement, but will send value
|
||||
{
|
||||
NT_Subscriber subHandle = nt::Handle{0, 2, nt::Handle::kSubscriber};
|
||||
std::vector<net::ClientMessage> msgs;
|
||||
msgs.emplace_back(net::ClientMessage{
|
||||
net::SubscribeMsg{subHandle, {{"test"}}, PubSubOptions{}}});
|
||||
server.ProcessIncomingText(id, EncodeText(msgs));
|
||||
}
|
||||
|
||||
server.SendValues(id, 200);
|
||||
}
|
||||
|
||||
TEST_F(ServerImplTest, ZeroTimestampNegativeTime) {
|
||||
// publish before client connect
|
||||
server.SetLocal(&local);
|
||||
NT_Publisher pubHandle = nt::Handle{0, 1, nt::Handle::kPublisher};
|
||||
NT_Topic topicHandle = nt::Handle{0, 1, nt::Handle::kTopic};
|
||||
NT_Subscriber subHandle = nt::Handle{0, 1, nt::Handle::kSubscriber};
|
||||
Value defaultValue = Value::MakeDouble(1.0, 10);
|
||||
defaultValue.SetTime(0);
|
||||
defaultValue.SetServerTime(0);
|
||||
Value value = Value::MakeDouble(5, -10);
|
||||
{
|
||||
::testing::InSequence seq;
|
||||
EXPECT_CALL(local, NetworkAnnounce("test", "double", wpi::json::object(),
|
||||
pubHandle))
|
||||
.WillOnce(Return(topicHandle));
|
||||
EXPECT_CALL(local, NetworkSetValue(topicHandle, defaultValue));
|
||||
EXPECT_CALL(local, NetworkSetValue(topicHandle, value));
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<net::ClientMessage> msgs;
|
||||
msgs.emplace_back(net::ClientMessage{net::PublishMsg{
|
||||
pubHandle, topicHandle, "test", "double", wpi::json::object(), {}}});
|
||||
msgs.emplace_back(
|
||||
net::ClientMessage{net::ClientValueMsg{pubHandle, defaultValue}});
|
||||
msgs.emplace_back(
|
||||
net::ClientMessage{net::SubscribeMsg{subHandle, {"test"}, {}}});
|
||||
server.HandleLocal(msgs);
|
||||
}
|
||||
|
||||
// client connect; it should get already-published topic as soon as it
|
||||
// subscribes
|
||||
::testing::StrictMock<net::MockWireConnection> wire;
|
||||
MockSetPeriodicFunc setPeriodic;
|
||||
{
|
||||
::testing::InSequence seq;
|
||||
EXPECT_CALL(wire, Flush()); // AddClient()
|
||||
}
|
||||
auto [name, id] = server.AddClient("test", "connInfo", false, wire,
|
||||
setPeriodic.AsStdFunction());
|
||||
|
||||
// publish and send non-default value with negative time offset
|
||||
{
|
||||
NT_Subscriber pubHandle2 = nt::Handle{0, 2, nt::Handle::kPublisher};
|
||||
std::vector<net::ClientMessage> msgs;
|
||||
msgs.emplace_back(net::ClientMessage{net::PublishMsg{
|
||||
pubHandle2, topicHandle, "test", "double", wpi::json::object(), {}}});
|
||||
server.ProcessIncomingText(id, EncodeText(msgs));
|
||||
msgs.clear();
|
||||
msgs.emplace_back(
|
||||
net::ClientMessage{net::ClientValueMsg{pubHandle2, value}});
|
||||
server.ProcessIncomingBinary(id, EncodeServerBinary(msgs));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -172,14 +172,15 @@ TEST_F(WireEncoderTextTest, MessageSubscribe) {
|
||||
ASSERT_TRUE(net::WireEncodeText(os, msg));
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"subscribe\",\"params\":{"
|
||||
"\"options\":{},\"topics\":[\"a\",\"b\"],\"subuid\":5}}");
|
||||
"\"options\":{},\"topics\":[\"a\",\"b\"],\"subuid\":402653189}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, MessageUnsubscribe) {
|
||||
net::ClientMessage msg{
|
||||
net::UnsubscribeMsg{Handle{0, 5, Handle::kSubscriber}}};
|
||||
ASSERT_TRUE(net::WireEncodeText(os, msg));
|
||||
ASSERT_EQ(os.str(), "{\"method\":\"unsubscribe\",\"params\":{\"subuid\":5}}");
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"unsubscribe\",\"params\":{\"subuid\":402653189}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, MessageAnnounce) {
|
||||
|
||||
@@ -183,6 +183,8 @@ static void DisplayGui() {
|
||||
ImGui::Text("v%s", GetWPILibVersion());
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Save location: %s", glass::GetStorageDir().c_str());
|
||||
ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate,
|
||||
ImGui::GetIO().Framerate);
|
||||
if (ImGui::Button("Close")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
84
processstarter/build.gradle
Normal file
84
processstarter/build.gradle
Normal file
@@ -0,0 +1,84 @@
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
if (!project.hasProperty('onlylinuxathena')) {
|
||||
|
||||
description = "Process Starter"
|
||||
|
||||
apply plugin: 'cpp'
|
||||
apply plugin: 'objective-cpp'
|
||||
apply plugin: 'visual-studio'
|
||||
apply plugin: 'edu.wpi.first.NativeUtils'
|
||||
|
||||
ext {
|
||||
nativeName = 'processstarter'
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/shared/config.gradle"
|
||||
|
||||
// Replace shared crt with static crt.
|
||||
// Note this means no wpilib binaries can be dependencies
|
||||
nativeUtils.platformConfigs.named(nativeUtils.wpi.platforms.windowsx64).configure {
|
||||
cppCompiler.debugArgs.remove('/MDd')
|
||||
cppCompiler.debugArgs.add('/MTd')
|
||||
cppCompiler.releaseArgs.remove('/MD')
|
||||
cppCompiler.releaseArgs.add('/MT')
|
||||
}
|
||||
|
||||
project(':').libraryBuild.dependsOn build
|
||||
|
||||
model {
|
||||
components {
|
||||
"${nativeName}"(NativeExecutableSpec) {
|
||||
baseName = 'processstarter'
|
||||
binaries.all {
|
||||
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
|
||||
it.buildable = false
|
||||
return
|
||||
}
|
||||
if (it.targetPlatform.operatingSystem.isMacOsX()) {
|
||||
it.sources {
|
||||
macObjCpp(ObjectiveCppSourceSet) {
|
||||
source {
|
||||
srcDirs 'src/main/native/osx'
|
||||
include '**/*.mm'
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs 'src/main/native/include'
|
||||
include '**/*.h'
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (it.targetPlatform.operatingSystem.isLinux()) {
|
||||
it.sources {
|
||||
linuxCpp(CppSourceSet) {
|
||||
source {
|
||||
srcDirs 'src/main/native/linux'
|
||||
include '**/*.cpp'
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs 'src/main/native/include'
|
||||
include '**/*.h'
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (it.targetPlatform.operatingSystem.isWindows()) {
|
||||
it.sources {
|
||||
windowsCpp(CppSourceSet) {
|
||||
source {
|
||||
srcDirs 'src/main/native/windows'
|
||||
include '**/*.cpp'
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs 'src/main/native/include'
|
||||
include '**/*.h'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply from: 'publish.gradle'
|
||||
}
|
||||
67
processstarter/publish.gradle
Normal file
67
processstarter/publish.gradle
Normal file
@@ -0,0 +1,67 @@
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
def baseArtifactId = 'processstarter'
|
||||
def artifactGroupId = 'edu.wpi.first.tools'
|
||||
def zipBaseName = '_GROUP_edu_wpi_first_tools_ID_processstarter_CLS'
|
||||
|
||||
def outputsFolder = file("$project.buildDir/outputs")
|
||||
|
||||
model {
|
||||
tasks {
|
||||
// Create the run task.
|
||||
$.components.processstarter.binaries.each { bin ->
|
||||
if (bin.buildable && bin.name.toLowerCase().contains("debug") && nativeUtils.isNativeDesktopPlatform(bin.targetPlatform)) {
|
||||
Task run = project.tasks.create("run", Exec) {
|
||||
commandLine bin.tasks.install.runScriptFile.get().asFile.toString()
|
||||
}
|
||||
run.dependsOn bin.tasks.install
|
||||
}
|
||||
}
|
||||
}
|
||||
publishing {
|
||||
def processstarterTaskList = []
|
||||
$.components.each { component ->
|
||||
component.binaries.each { binary ->
|
||||
if (binary in NativeExecutableBinarySpec && binary.component.name.contains("processstarter")) {
|
||||
if (binary.buildable && (binary.name.contains('Release') || binary.name.contains('release'))) {
|
||||
// We are now in the binary that we want.
|
||||
// This is the default application path for the ZIP task.
|
||||
def applicationPath = binary.executable.file
|
||||
|
||||
// Create the ZIP.
|
||||
def task = project.tasks.create("copyprocessstarterExecutable" + binary.targetPlatform.architecture.name, Zip) {
|
||||
description("Copies the processstarter executable to the outputs directory.")
|
||||
destinationDirectory = outputsFolder
|
||||
|
||||
archiveBaseName = '_M_' + zipBaseName
|
||||
duplicatesStrategy = 'exclude'
|
||||
classifier = nativeUtils.getPublishClassifier(binary)
|
||||
|
||||
from(licenseFile) {
|
||||
into '/'
|
||||
}
|
||||
|
||||
from(applicationPath)
|
||||
}
|
||||
|
||||
task.dependsOn binary.tasks.link
|
||||
processstarterTaskList.add(task)
|
||||
project.build.dependsOn task
|
||||
project.artifacts { task }
|
||||
addTaskToCopyAllOutputs(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publications {
|
||||
processstarter(MavenPublication) {
|
||||
processstarterTaskList.each { artifact it }
|
||||
|
||||
artifactId = baseArtifactId
|
||||
groupId = artifactGroupId
|
||||
version wpilibVersioning.version.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
86
processstarter/src/main/native/linux/main.cpp
Normal file
86
processstarter/src/main/native/linux/main.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
// 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 <poll.h>
|
||||
#include <spawn.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
char path[PATH_MAX];
|
||||
char dest[PATH_MAX];
|
||||
std::memset(dest, 0, sizeof(dest)); // readlink does not null terminate!
|
||||
pid_t pid = getpid();
|
||||
std::snprintf(path, PATH_MAX, "/proc/%d/exe", pid);
|
||||
int readlink_len = readlink(path, dest, PATH_MAX);
|
||||
if (readlink_len < 0) {
|
||||
std::perror("readlink");
|
||||
return 1;
|
||||
} else if (readlink_len == PATH_MAX) {
|
||||
std::printf("Truncation occured\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::filesystem::path exePath{dest};
|
||||
if (exePath.empty()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!exePath.has_stem()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!exePath.has_parent_path()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::filesystem::path jarPath{exePath};
|
||||
jarPath.replace_extension("jar");
|
||||
std::filesystem::path parentPath{exePath.parent_path()};
|
||||
|
||||
if (!parentPath.has_parent_path()) {
|
||||
return 1;
|
||||
}
|
||||
std::filesystem::path toolsFolder{parentPath.parent_path()};
|
||||
|
||||
std::filesystem::path Java = toolsFolder / "jdk" / "bin" / "java";
|
||||
|
||||
pid = 0;
|
||||
std::string data = jarPath;
|
||||
std::string jarArg = "-jar";
|
||||
char* const arguments[] = {jarArg.data(), data.data(), nullptr};
|
||||
|
||||
int status =
|
||||
posix_spawn(&pid, Java.c_str(), nullptr, nullptr, arguments, environ);
|
||||
if (status != 0) {
|
||||
char* home = std::getenv("JAVA_HOME");
|
||||
std::string javaLocal = "java";
|
||||
if (home != nullptr) {
|
||||
std::filesystem::path javaHomePath{home};
|
||||
javaHomePath /= "bin";
|
||||
javaHomePath /= "java";
|
||||
javaLocal = javaHomePath;
|
||||
}
|
||||
|
||||
status = posix_spawn(&pid, javaLocal.c_str(), nullptr, nullptr, arguments,
|
||||
environ);
|
||||
if (status != 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int childPid = syscall(SYS_pidfd_open, pid, 0);
|
||||
if (childPid <= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct pollfd pfd = {childPid, POLLIN, 0};
|
||||
return poll(&pfd, 1, 3000);
|
||||
}
|
||||
80
processstarter/src/main/native/osx/main.mm
Normal file
80
processstarter/src/main/native/osx/main.mm
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
NSString* exePathPlat = [[NSBundle mainBundle] bundlePath];
|
||||
NSString* identifier = [[NSBundle mainBundle] bundleIdentifier];
|
||||
if (identifier == nil) {
|
||||
exePathPlat = [[NSBundle mainBundle] executablePath];
|
||||
}
|
||||
|
||||
std::filesystem::path exePath{[exePathPlat UTF8String]};
|
||||
if (exePath.empty()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!exePath.has_stem()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!exePath.has_parent_path()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::filesystem::path jarPath{exePath};
|
||||
jarPath.replace_extension("jar");
|
||||
std::filesystem::path parentPath{exePath.parent_path()};
|
||||
|
||||
if (!parentPath.has_parent_path()) {
|
||||
return 1;
|
||||
}
|
||||
std::filesystem::path toolsFolder{parentPath.parent_path()};
|
||||
|
||||
std::filesystem::path java = toolsFolder / "jdk" / "bin" / "java";
|
||||
|
||||
NSArray<NSString*>* Arguments =
|
||||
@[ @"-jar", [NSString stringWithFormat:@"%s", jarPath.c_str()] ];
|
||||
|
||||
NSTask* task = [[NSTask alloc] init];
|
||||
task.launchPath = [NSString stringWithFormat:@"%s", java.c_str()];
|
||||
task.arguments = Arguments;
|
||||
task.terminationHandler = ^(NSTask* t) {
|
||||
(void)t;
|
||||
CFRunLoopStop(CFRunLoopGetMain());
|
||||
};
|
||||
|
||||
if (![task launchAndReturnError:nil]) {
|
||||
task.terminationHandler = nil;
|
||||
|
||||
NSString* javaHome =
|
||||
[[[NSProcessInfo processInfo] environment] objectForKey:@"JAVA_HOME"];
|
||||
task = [[NSTask alloc] init];
|
||||
task.launchPath = @"java";
|
||||
if (javaHome != nil) {
|
||||
std::filesystem::path javaHomePath{[javaHome UTF8String]};
|
||||
javaHomePath /= "bin";
|
||||
javaHomePath /= "java";
|
||||
task.launchPath = [NSString stringWithFormat:@"%s", javaHomePath.c_str()];
|
||||
}
|
||||
task.arguments = Arguments;
|
||||
task.terminationHandler = ^(NSTask* t) {
|
||||
(void)t;
|
||||
CFRunLoopStop(CFRunLoopGetMain());
|
||||
};
|
||||
|
||||
if (![task launchAndReturnError:nil]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 3, false);
|
||||
|
||||
return task.running ? 0 : 1;
|
||||
}
|
||||
85
processstarter/src/main/native/windows/main.cpp
Normal file
85
processstarter/src/main/native/windows/main.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
// 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 <filesystem>
|
||||
|
||||
#include "Windows.h"
|
||||
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
||||
LPTSTR pCmdLine, int nCmdShow) {
|
||||
DWORD Status;
|
||||
WCHAR ExePathRaw[1024];
|
||||
Status = GetModuleFileNameW(NULL, ExePathRaw, 1024);
|
||||
if (Status == 0) {
|
||||
DWORD LastError = GetLastError();
|
||||
return LastError;
|
||||
}
|
||||
|
||||
std::filesystem::path ExePath{ExePathRaw};
|
||||
if (ExePath.empty()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ExePath.has_stem()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ExePath.has_parent_path()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::filesystem::path JarPath{ExePath};
|
||||
JarPath.replace_extension(L"jar");
|
||||
std::filesystem::path ParentPath{ExePath.parent_path()};
|
||||
|
||||
if (!ParentPath.has_parent_path()) {
|
||||
return 1;
|
||||
}
|
||||
std::filesystem::path ToolsFolder{ParentPath.parent_path()};
|
||||
|
||||
std::filesystem::path Javaw = ToolsFolder / L"jdk" / L"bin" / L"javaw.exe";
|
||||
|
||||
std::wstring ToRun = L" -jar \"";
|
||||
ToRun += JarPath;
|
||||
ToRun += L"\"";
|
||||
|
||||
STARTUPINFOW StartupInfo;
|
||||
PROCESS_INFORMATION ProcessInfo;
|
||||
|
||||
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
|
||||
StartupInfo.cb = sizeof(StartupInfo);
|
||||
ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
|
||||
|
||||
if (!CreateProcessW(Javaw.c_str(), ToRun.data(), NULL, NULL, FALSE, 0, NULL,
|
||||
NULL, &StartupInfo, &ProcessInfo)) {
|
||||
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
|
||||
StartupInfo.cb = sizeof(StartupInfo);
|
||||
ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
|
||||
|
||||
ToRun = L" -jar \"";
|
||||
ToRun += JarPath;
|
||||
ToRun += L"\"";
|
||||
|
||||
Status = GetEnvironmentVariableW(L"JAVA_HOME", ExePathRaw, 1024);
|
||||
std::wstring JavawLocal = L"javaw";
|
||||
if (Status != 0 && Status < 1024) {
|
||||
std::filesystem::path JavaHomePath{ExePathRaw};
|
||||
JavaHomePath /= "bin";
|
||||
JavaHomePath /= "javaw.exe";
|
||||
JavawLocal = JavaHomePath;
|
||||
}
|
||||
|
||||
if (!CreateProcessW(JavawLocal.c_str(), ToRun.data(), NULL, NULL, FALSE, 0,
|
||||
NULL, NULL, &StartupInfo, &ProcessInfo)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
Status =
|
||||
WaitForSingleObject(ProcessInfo.hProcess, 3000); // Wait for 3 seconds
|
||||
CloseHandle(ProcessInfo.hProcess);
|
||||
CloseHandle(ProcessInfo.hThread);
|
||||
|
||||
return Status == WAIT_TIMEOUT ? 0 : 1;
|
||||
}
|
||||
@@ -127,6 +127,8 @@ static void DisplayGui() {
|
||||
static_cast<int>(multicastResolver->HasImplementation()));
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Save location: %s", glass::GetStorageDir().c_str());
|
||||
ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate,
|
||||
ImGui::GetIO().Framerate);
|
||||
if (ImGui::Button("Close")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ include 'docs'
|
||||
include 'msvcruntime'
|
||||
include 'ntcoreffi'
|
||||
include 'apriltag'
|
||||
include 'processstarter'
|
||||
|
||||
buildCache {
|
||||
def cred = {
|
||||
|
||||
@@ -42,6 +42,46 @@ task checkTemplates(type: Task) {
|
||||
}
|
||||
}
|
||||
|
||||
def tagList = [
|
||||
/* --- Categories --- */
|
||||
// On-RIO image processing
|
||||
"Vision",
|
||||
// Command-based
|
||||
"Command-based",
|
||||
// Romi
|
||||
"Romi",
|
||||
// Extremely simple programs showcasing a single hardware API
|
||||
"Hardware",
|
||||
// Full robot, with multiple mechanisms
|
||||
"Complete Robot",
|
||||
// A single mechanism in the Robot class
|
||||
"Basic Robot",
|
||||
|
||||
/* --- Mechanisms --- */
|
||||
"Intake", "Flywheel", "Elevator", "Arm", "Differential Drive", "Mecanum Drive",
|
||||
"Swerve Drive",
|
||||
|
||||
/* --- Telemetry --- */
|
||||
"SmartDashboard", "Shuffleboard", "Sendable", "DataLog",
|
||||
|
||||
/* --- Controls --- */
|
||||
"PID", "State-Space", "Ramsete", "Path Following", "Trajectory", "SysId",
|
||||
"Simulation", "Trapezoid Profile", "Profiled PID", "Odometry", "LQR",
|
||||
"Pose Estimator",
|
||||
|
||||
/* --- Hardware --- */
|
||||
"Analog", "Ultrasonic", "Gyro", "Pneumatics", "I2C", "Duty Cycle", "PDP", "DMA", "Relay",
|
||||
"AddressableLEDs", "HAL", "Encoder", "Smart Motor Controller", "Digital Input",
|
||||
"Digital Output",
|
||||
|
||||
/* --- HID --- */
|
||||
"XboxController", "PS4Controller", "Joystick",
|
||||
|
||||
/* --- Misc --- */
|
||||
/* (try to keep this section minimal) */
|
||||
"EventLoop", "AprilTags", "Mechanism2d", "Preferences",
|
||||
]
|
||||
|
||||
task checkExamples(type: Task) {
|
||||
doLast {
|
||||
def parsedJson = new groovy.json.JsonSlurper().parseText(exampleFile.text)
|
||||
@@ -50,6 +90,7 @@ task checkExamples(type: Task) {
|
||||
assert it.name != null
|
||||
assert it.description != null
|
||||
assert it.tags != null
|
||||
assert it.tags.findAll { !tagList.contains(it) }.empty
|
||||
assert it.foldername != null
|
||||
assert it.gradlebase != null
|
||||
assert it.commandversion != null
|
||||
|
||||
@@ -41,6 +41,14 @@ if (!project.hasProperty('skipJavaFormat')) {
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
json {
|
||||
target fileTree('.') {
|
||||
include '**/*.json'
|
||||
exclude '**/build/**', '**/build-*/**'
|
||||
}
|
||||
gson()
|
||||
.indentWithSpaces(2)
|
||||
}
|
||||
format 'xml', {
|
||||
target fileTree('.') {
|
||||
include '**/*.xml'
|
||||
|
||||
@@ -487,7 +487,7 @@ void GlfwSystemJoystick::GetData(HALJoystickData* data, bool mapGamepad) const {
|
||||
}
|
||||
} else {
|
||||
std::memcpy(data->axes.axes, sysAxes,
|
||||
data->axes.count * sizeof(&data->axes.axes[0]));
|
||||
data->axes.count * sizeof(data->axes.axes[0]));
|
||||
}
|
||||
|
||||
data->povs.count = data->desc.povCount;
|
||||
|
||||
@@ -116,7 +116,8 @@ __declspec(dllexport)
|
||||
}
|
||||
});
|
||||
|
||||
if (!gui::Initialize("Robot Simulation", 1280, 720)) {
|
||||
if (!gui::Initialize("Robot Simulation", 1280, 720,
|
||||
ImGuiConfigFlags_DockingEnable)) {
|
||||
return 0;
|
||||
}
|
||||
HAL_RegisterExtensionListener(
|
||||
|
||||
30
upstream_utils/eigen_patches/0002-Intellisense-fix.patch
Normal file
30
upstream_utils/eigen_patches/0002-Intellisense-fix.patch
Normal file
@@ -0,0 +1,30 @@
|
||||
From 94882f4460897f92dfe9f95ec33629094f8e76a2 Mon Sep 17 00:00:00 2001
|
||||
From: Peter Johnson <johnson.peter@gmail.com>
|
||||
Date: Fri, 20 Jan 2023 23:41:56 -0800
|
||||
Subject: [PATCH 2/2] Intellisense fix
|
||||
|
||||
---
|
||||
Eigen/src/Core/util/Macros.h | 10 ++++++++++
|
||||
1 file changed, 10 insertions(+)
|
||||
|
||||
diff --git a/Eigen/src/Core/util/Macros.h b/Eigen/src/Core/util/Macros.h
|
||||
index 986c3d4..81986b9 100644
|
||||
--- a/Eigen/src/Core/util/Macros.h
|
||||
+++ b/Eigen/src/Core/util/Macros.h
|
||||
@@ -58,6 +58,16 @@
|
||||
// Compiler identification, EIGEN_COMP_*
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
+/// \internal Disable NEON features in Intellisense
|
||||
+#if __INTELLISENSE__
|
||||
+#ifdef __ARM_NEON
|
||||
+#undef __ARM_NEON
|
||||
+#endif
|
||||
+#ifdef __ARM_NEON__
|
||||
+#undef __ARM_NEON__
|
||||
+#endif
|
||||
+#endif
|
||||
+
|
||||
/// \internal EIGEN_COMP_GNUC set to 1 for all compilers compatible with GCC
|
||||
#ifdef __GNUC__
|
||||
#define EIGEN_COMP_GNUC (__GNUC__*10+__GNUC_MINOR__)
|
||||
@@ -105,7 +105,7 @@ def main():
|
||||
|
||||
# Apply patches to upstream Git repo
|
||||
os.chdir(upstream_root)
|
||||
for f in ["0001-Disable-warnings.patch"]:
|
||||
for f in ["0001-Disable-warnings.patch", "0002-Intellisense-fix.patch"]:
|
||||
git_am(os.path.join(wpilib_root, "upstream_utils/eigen_patches", f))
|
||||
|
||||
# Delete old install
|
||||
|
||||
@@ -4,9 +4,13 @@
|
||||
|
||||
#include "wpigui.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <IconsFontAwesome6.h>
|
||||
@@ -88,6 +92,8 @@ static void IniReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler,
|
||||
impl->userScale = num;
|
||||
} else if (std::strncmp(lineStr, "style=", 6) == 0) {
|
||||
impl->style = num;
|
||||
} else if (std::strncmp(lineStr, "fps=", 4) == 0) {
|
||||
impl->fps = num;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,9 +104,10 @@ static void IniWriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler,
|
||||
}
|
||||
out_buf->appendf(
|
||||
"[MainWindow][GLOBAL]\nwidth=%d\nheight=%d\nmaximized=%d\n"
|
||||
"xpos=%d\nypos=%d\nuserScale=%d\nstyle=%d\n\n",
|
||||
"xpos=%d\nypos=%d\nuserScale=%d\nstyle=%d\nfps=%d\n\n",
|
||||
gContext->width, gContext->height, gContext->maximized ? 1 : 0,
|
||||
gContext->xPos, gContext->yPos, gContext->userScale, gContext->style);
|
||||
gContext->xPos, gContext->yPos, gContext->userScale, gContext->style,
|
||||
gContext->fps);
|
||||
}
|
||||
|
||||
void gui::CreateContext() {
|
||||
@@ -331,6 +338,8 @@ bool gui::Initialize(const char* title, int width, int height,
|
||||
void gui::Main() {
|
||||
// Main loop
|
||||
while (!glfwWindowShouldClose(gContext->window) && !gContext->exit) {
|
||||
double startTime = glfwGetTime();
|
||||
|
||||
// Poll and handle events (inputs, window resize, etc.)
|
||||
glfwPollEvents();
|
||||
gContext->isPlatformRendering = true;
|
||||
@@ -351,6 +360,15 @@ void gui::Main() {
|
||||
io.WantSaveIniSettings = false; // reset flag
|
||||
}
|
||||
}
|
||||
|
||||
// if FPS limiting, sleep until next frame time
|
||||
if (gContext->fps != 0) {
|
||||
double sleepTime = (1.0 / gContext->fps) - (glfwGetTime() - startTime);
|
||||
if (sleepTime > 1e-6) {
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::microseconds(static_cast<int64_t>(sleepTime * 1e6)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save (if custom save)
|
||||
@@ -477,6 +495,10 @@ void gui::SetStyle(Style style) {
|
||||
}
|
||||
}
|
||||
|
||||
void gui::SetFPS(int fps) {
|
||||
gContext->fps = fps;
|
||||
}
|
||||
|
||||
void gui::SetClearColor(ImVec4 color) {
|
||||
gContext->clearColor = color;
|
||||
}
|
||||
@@ -539,6 +561,27 @@ void gui::EmitViewMenu() {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Frame Rate")) {
|
||||
bool selected;
|
||||
selected = gContext->fps == 0;
|
||||
if (ImGui::MenuItem("vsync", nullptr, &selected)) {
|
||||
gContext->fps = 0;
|
||||
}
|
||||
selected = gContext->fps == 30;
|
||||
if (ImGui::MenuItem("30 fps", nullptr, &selected)) {
|
||||
gContext->fps = 30;
|
||||
}
|
||||
selected = gContext->fps == 60;
|
||||
if (ImGui::MenuItem("60 fps", nullptr, &selected)) {
|
||||
gContext->fps = 60;
|
||||
}
|
||||
selected = gContext->fps == 120;
|
||||
if (ImGui::MenuItem("120 fps", nullptr, &selected)) {
|
||||
gContext->fps = 120;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (!gContext->saveSettings) {
|
||||
ImGui::MenuItem("Reset UI on Exit?", nullptr, &gContext->resetOnExit);
|
||||
}
|
||||
|
||||
@@ -162,6 +162,13 @@ enum Style { kStyleClassic = 0, kStyleDark, kStyleLight };
|
||||
*/
|
||||
void SetStyle(Style style);
|
||||
|
||||
/**
|
||||
* Sets the FPS limit. Using this function makes this setting persistent.
|
||||
*
|
||||
* @param fps FPS (0=vsync)
|
||||
*/
|
||||
void SetFPS(int fps);
|
||||
|
||||
/**
|
||||
* Sets the clear (background) color.
|
||||
*
|
||||
|
||||
@@ -24,6 +24,7 @@ struct SavedSettings {
|
||||
int yPos = -1;
|
||||
int userScale = 2;
|
||||
int style = 0;
|
||||
int fps = 120;
|
||||
};
|
||||
|
||||
constexpr int kFontScaledLevels = 9;
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
{
|
||||
"fileName": "WPILibNewCommands.json",
|
||||
"name": "WPILib-New-Commands",
|
||||
"version": "1.0.0",
|
||||
"uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266",
|
||||
"mavenUrls": [],
|
||||
"jsonUrl": "",
|
||||
"javaDependencies": [
|
||||
{
|
||||
"groupId": "edu.wpi.first.wpilibNewCommands",
|
||||
"artifactId": "wpilibNewCommands-java",
|
||||
"version": "wpilib"
|
||||
}
|
||||
],
|
||||
"jniDependencies": [],
|
||||
"cppDependencies": [
|
||||
{
|
||||
"groupId": "edu.wpi.first.wpilibNewCommands",
|
||||
"artifactId": "wpilibNewCommands-cpp",
|
||||
"version": "wpilib",
|
||||
"libName": "wpilibNewCommands",
|
||||
"headerClassifier": "headers",
|
||||
"sourcesClassifier": "sources",
|
||||
"sharedLibrary": true,
|
||||
"skipInvalidPlatforms": true,
|
||||
"binaryPlatforms": [
|
||||
"linuxathena",
|
||||
"linuxarm32",
|
||||
"linuxarm64",
|
||||
"windowsx86-64",
|
||||
"windowsx86",
|
||||
"linuxx86-64",
|
||||
"osxuniversal"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"fileName": "WPILibNewCommands.json",
|
||||
"name": "WPILib-New-Commands",
|
||||
"version": "1.0.0",
|
||||
"uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266",
|
||||
"mavenUrls": [],
|
||||
"jsonUrl": "",
|
||||
"javaDependencies": [
|
||||
{
|
||||
"groupId": "edu.wpi.first.wpilibNewCommands",
|
||||
"artifactId": "wpilibNewCommands-java",
|
||||
"version": "wpilib"
|
||||
}
|
||||
],
|
||||
"jniDependencies": [],
|
||||
"cppDependencies": [
|
||||
{
|
||||
"groupId": "edu.wpi.first.wpilibNewCommands",
|
||||
"artifactId": "wpilibNewCommands-cpp",
|
||||
"version": "wpilib",
|
||||
"libName": "wpilibNewCommands",
|
||||
"headerClassifier": "headers",
|
||||
"sourcesClassifier": "sources",
|
||||
"sharedLibrary": true,
|
||||
"skipInvalidPlatforms": true,
|
||||
"binaryPlatforms": [
|
||||
"linuxathena",
|
||||
"linuxarm32",
|
||||
"linuxarm64",
|
||||
"windowsx86-64",
|
||||
"windowsx86",
|
||||
"linuxx86-64",
|
||||
"osxuniversal"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ model {
|
||||
project(':ntcore').addNtcoreJniDependency(it)
|
||||
lib project: ':wpinet', library: 'wpinetJNIShared', linkage: 'shared'
|
||||
lib project: ':wpiutil', library: 'wpiutilJNIShared', linkage: 'shared'
|
||||
lib project: ':wpimath', library: 'wpimathJNIShared', linkage: 'shared'
|
||||
project(':hal').addHalJniDependency(it)
|
||||
}
|
||||
|
||||
|
||||
@@ -332,8 +332,7 @@ public class MecanumControllerCommand extends CommandBase {
|
||||
m_prevSpeeds =
|
||||
m_kinematics.toWheelSpeeds(new ChassisSpeeds(initialXVelocity, initialYVelocity, 0.0));
|
||||
|
||||
m_timer.reset();
|
||||
m_timer.start();
|
||||
m_timer.restart();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -143,8 +143,7 @@ public class RamseteCommand extends CommandBase {
|
||||
initialState.velocityMetersPerSecond,
|
||||
0,
|
||||
initialState.curvatureRadPerMeter * initialState.velocityMetersPerSecond));
|
||||
m_timer.reset();
|
||||
m_timer.start();
|
||||
m_timer.restart();
|
||||
if (m_usePID) {
|
||||
m_leftController.reset();
|
||||
m_rightController.reset();
|
||||
|
||||
@@ -210,8 +210,7 @@ public class SwerveControllerCommand extends CommandBase {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
m_timer.reset();
|
||||
m_timer.start();
|
||||
m_timer.restart();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,8 +39,7 @@ public class TrapezoidProfileCommand extends CommandBase {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
m_timer.reset();
|
||||
m_timer.start();
|
||||
m_timer.restart();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,8 +30,7 @@ public class WaitCommand extends CommandBase {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
m_timer.reset();
|
||||
m_timer.start();
|
||||
m_timer.restart();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,7 +9,7 @@ import edu.wpi.first.wpilibj.event.EventLoop;
|
||||
import edu.wpi.first.wpilibj2.command.CommandScheduler;
|
||||
|
||||
/**
|
||||
* A subclass of {@link Joystick} with {@link Trigger} factories for command-based.
|
||||
* A version of {@link Joystick} with {@link Trigger} factories for command-based.
|
||||
*
|
||||
* @see Joystick
|
||||
*/
|
||||
|
||||
@@ -9,7 +9,7 @@ import edu.wpi.first.wpilibj.event.EventLoop;
|
||||
import edu.wpi.first.wpilibj2.command.CommandScheduler;
|
||||
|
||||
/**
|
||||
* A subclass of {@link XboxController} with {@link Trigger} factories for command-based.
|
||||
* A version of {@link XboxController} with {@link Trigger} factories for command-based.
|
||||
*
|
||||
* @see XboxController
|
||||
*/
|
||||
|
||||
@@ -205,7 +205,7 @@ public class Trigger implements BooleanSupplier {
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a command when the condition changes from `true` to the low state.
|
||||
* Toggles a command when the condition changes from `true` to `false`.
|
||||
*
|
||||
* @param command the command to toggle
|
||||
* @return this trigger, so calls can be chained
|
||||
|
||||
@@ -257,8 +257,7 @@ void MecanumControllerCommand::Initialize() {
|
||||
m_prevSpeeds = m_kinematics.ToWheelSpeeds(
|
||||
frc::ChassisSpeeds{initialXVelocity, initialYVelocity, 0_rad_per_s});
|
||||
|
||||
m_timer.Reset();
|
||||
m_timer.Start();
|
||||
m_timer.Restart();
|
||||
if (m_usePID) {
|
||||
m_frontLeftController->Reset();
|
||||
m_rearLeftController->Reset();
|
||||
|
||||
@@ -92,8 +92,7 @@ void RamseteCommand::Initialize() {
|
||||
m_prevSpeeds = m_kinematics.ToWheelSpeeds(
|
||||
frc::ChassisSpeeds{initialState.velocity, 0_mps,
|
||||
initialState.velocity * initialState.curvature});
|
||||
m_timer.Reset();
|
||||
m_timer.Start();
|
||||
m_timer.Restart();
|
||||
if (m_usePID) {
|
||||
m_leftController->Reset();
|
||||
m_rightController->Reset();
|
||||
|
||||
@@ -15,8 +15,7 @@ WaitCommand::WaitCommand(units::second_t duration) : m_duration{duration} {
|
||||
}
|
||||
|
||||
void WaitCommand::Initialize() {
|
||||
m_timer.Reset();
|
||||
m_timer.Start();
|
||||
m_timer.Restart();
|
||||
}
|
||||
|
||||
void WaitCommand::End(bool interrupted) {
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -156,13 +158,15 @@ namespace cmd {
|
||||
* @param selector the selector function
|
||||
* @param commands map of commands to select from
|
||||
*/
|
||||
template <typename Key>
|
||||
[[nodiscard]] CommandPtr Select(
|
||||
std::function<Key()> selector,
|
||||
std::vector<std::pair<Key, CommandPtr>> commands) {
|
||||
return SelectCommand(std::move(selector),
|
||||
CommandPtr::UnwrapVector(std::move(commands)))
|
||||
.ToPtr();
|
||||
template <typename Key, class... Types>
|
||||
[[nodiscard]] CommandPtr Select(std::function<Key()> selector,
|
||||
std::pair<Key, Types>&&... commands) {
|
||||
std::vector<std::pair<Key, std::unique_ptr<Command>>> vec;
|
||||
|
||||
((void)vec.emplace_back(commands.first, std::move(commands.second).Unwrap()),
|
||||
...);
|
||||
|
||||
return SelectCommand(std::move(selector), std::move(vec)).ToPtr();
|
||||
}
|
||||
|
||||
// Command Groups
|
||||
|
||||
@@ -150,8 +150,7 @@ void SwerveControllerCommand<NumModules>::Initialize() {
|
||||
return m_trajectory.States().back().pose.Rotation();
|
||||
};
|
||||
}
|
||||
m_timer.Reset();
|
||||
m_timer.Start();
|
||||
m_timer.Restart();
|
||||
}
|
||||
|
||||
template <size_t NumModules>
|
||||
|
||||
@@ -63,10 +63,7 @@ class TrapezoidProfileCommand
|
||||
this->AddRequirements(requirements);
|
||||
}
|
||||
|
||||
void Initialize() override {
|
||||
m_timer.Reset();
|
||||
m_timer.Start();
|
||||
}
|
||||
void Initialize() override { m_timer.Restart(); }
|
||||
|
||||
void Execute() override { m_output(m_profile.Calculate(m_timer.Get())); }
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace frc2 {
|
||||
/**
|
||||
* A subclass of {@link Joystick} with {@link Trigger} factories for
|
||||
* A version of {@link Joystick} with {@link Trigger} factories for
|
||||
* command-based.
|
||||
*
|
||||
* @see Joystick
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace frc2 {
|
||||
/**
|
||||
* A subclass of {@link PS4Controller} with {@link Trigger} factories for
|
||||
* A version of {@link PS4Controller} with {@link Trigger} factories for
|
||||
* command-based.
|
||||
*
|
||||
* @see PS4Controller
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace frc2 {
|
||||
/**
|
||||
* A subclass of {@link XboxController} with {@link Trigger} factories for
|
||||
* A version of {@link XboxController} with {@link Trigger} factories for
|
||||
* command-based.
|
||||
*
|
||||
* @see XboxController
|
||||
|
||||
@@ -194,8 +194,7 @@ class Trigger {
|
||||
Trigger ToggleOnFalse(Command* command);
|
||||
|
||||
/**
|
||||
* Toggles a command when the condition changes from `true` to the low
|
||||
* state.
|
||||
* Toggles a command when the condition changes from `true` to `false`.
|
||||
*
|
||||
* <p>Takes a raw pointer, and so is non-owning; users are responsible for the
|
||||
* lifespan of the command.
|
||||
|
||||
@@ -118,8 +118,7 @@ class MecanumControllerCommandTest {
|
||||
this::setWheelSpeeds,
|
||||
subsystem);
|
||||
|
||||
m_timer.reset();
|
||||
m_timer.start();
|
||||
m_timer.restart();
|
||||
|
||||
command.initialize();
|
||||
while (!command.isFinished()) {
|
||||
|
||||
@@ -114,8 +114,7 @@ class SwerveControllerCommandTest {
|
||||
this::setModuleStates,
|
||||
subsystem);
|
||||
|
||||
m_timer.reset();
|
||||
m_timer.start();
|
||||
m_timer.restart();
|
||||
|
||||
command.initialize();
|
||||
while (!command.isFinished()) {
|
||||
|
||||
@@ -112,8 +112,7 @@ TEST_F(MecanumControllerCommandTest, ReachesReference) {
|
||||
},
|
||||
{&subsystem});
|
||||
|
||||
m_timer.Reset();
|
||||
m_timer.Start();
|
||||
m_timer.Restart();
|
||||
|
||||
command.Initialize();
|
||||
while (!command.IsFinished()) {
|
||||
|
||||
@@ -88,8 +88,7 @@ TEST_F(SwerveControllerCommandTest, ReachesReference) {
|
||||
m_rotController,
|
||||
[&](auto moduleStates) { m_moduleStates = moduleStates; }, {&subsystem});
|
||||
|
||||
m_timer.Reset();
|
||||
m_timer.Start();
|
||||
m_timer.Restart();
|
||||
|
||||
command.Initialize();
|
||||
while (!command.IsFinished()) {
|
||||
|
||||
@@ -69,7 +69,7 @@ bool ADXRS450_Gyro::IsConnected() const {
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
static bool CalcParity(int v) {
|
||||
static bool CalcParity(uint32_t v) {
|
||||
bool parity = false;
|
||||
while (v != 0) {
|
||||
parity = !parity;
|
||||
@@ -87,7 +87,7 @@ static inline int BytesToIntBE(uint8_t* buf) {
|
||||
}
|
||||
|
||||
uint16_t ADXRS450_Gyro::ReadRegister(int reg) {
|
||||
int cmd = 0x80000000 | static_cast<int>(reg) << 17;
|
||||
uint32_t cmd = 0x80000000 | static_cast<int>(reg) << 17;
|
||||
if (!CalcParity(cmd)) {
|
||||
cmd |= 1u;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,14 @@ void Timer::Start() {
|
||||
}
|
||||
}
|
||||
|
||||
void Timer::Restart() {
|
||||
if (m_running) {
|
||||
Stop();
|
||||
}
|
||||
Reset();
|
||||
Start();
|
||||
}
|
||||
|
||||
void Timer::Stop() {
|
||||
if (m_running) {
|
||||
m_accumulatedTime = Get();
|
||||
|
||||
@@ -41,11 +41,11 @@ ElevatorSim::ElevatorSim(const DCMotor& gearbox, double gearing,
|
||||
m_simulateGravity(simulateGravity) {}
|
||||
|
||||
bool ElevatorSim::WouldHitLowerLimit(units::meter_t elevatorHeight) const {
|
||||
return elevatorHeight < m_minHeight;
|
||||
return elevatorHeight <= m_minHeight;
|
||||
}
|
||||
|
||||
bool ElevatorSim::WouldHitUpperLimit(units::meter_t elevatorHeight) const {
|
||||
return elevatorHeight > m_maxHeight;
|
||||
return elevatorHeight >= m_maxHeight;
|
||||
}
|
||||
|
||||
bool ElevatorSim::HasHitLowerLimit() const {
|
||||
|
||||
@@ -18,13 +18,12 @@ using namespace frc::sim;
|
||||
SingleJointedArmSim::SingleJointedArmSim(
|
||||
const LinearSystem<2, 1, 1>& system, const DCMotor& gearbox, double gearing,
|
||||
units::meter_t armLength, units::radian_t minAngle,
|
||||
units::radian_t maxAngle, units::kilogram_t armMass, bool simulateGravity,
|
||||
units::radian_t maxAngle, bool simulateGravity,
|
||||
const std::array<double, 1>& measurementStdDevs)
|
||||
: LinearSystemSim<2, 1, 1>(system, measurementStdDevs),
|
||||
m_r(armLength),
|
||||
m_armLen(armLength),
|
||||
m_minAngle(minAngle),
|
||||
m_maxAngle(maxAngle),
|
||||
m_armMass(armMass),
|
||||
m_gearbox(gearbox),
|
||||
m_gearing(gearing),
|
||||
m_simulateGravity(simulateGravity) {}
|
||||
@@ -32,12 +31,12 @@ SingleJointedArmSim::SingleJointedArmSim(
|
||||
SingleJointedArmSim::SingleJointedArmSim(
|
||||
const DCMotor& gearbox, double gearing, units::kilogram_square_meter_t moi,
|
||||
units::meter_t armLength, units::radian_t minAngle,
|
||||
units::radian_t maxAngle, units::kilogram_t armMass, bool simulateGravity,
|
||||
units::radian_t maxAngle, bool simulateGravity,
|
||||
const std::array<double, 1>& measurementStdDevs)
|
||||
: SingleJointedArmSim(
|
||||
LinearSystemId::SingleJointedArmSystem(gearbox, moi, gearing),
|
||||
gearbox, gearing, armLength, minAngle, maxAngle, armMass,
|
||||
simulateGravity, measurementStdDevs) {}
|
||||
gearbox, gearing, armLength, minAngle, maxAngle, simulateGravity,
|
||||
measurementStdDevs) {}
|
||||
|
||||
bool SingleJointedArmSim::WouldHitLowerLimit(units::radian_t armAngle) const {
|
||||
return armAngle <= m_minAngle;
|
||||
@@ -78,24 +77,37 @@ void SingleJointedArmSim::SetInputVoltage(units::volt_t voltage) {
|
||||
Vectord<2> SingleJointedArmSim::UpdateX(const Vectord<2>& currentXhat,
|
||||
const Vectord<1>& u,
|
||||
units::second_t dt) {
|
||||
// Horizontal case:
|
||||
// Torque = F * r = I * alpha
|
||||
// alpha = F * r / I
|
||||
// Since F = mg,
|
||||
// alpha = m * g * r / I
|
||||
// Finally, multiply RHS by cos(theta) to account for the arm angle
|
||||
// This acceleration is added to the linear system dynamics x-dot = Ax + Bu
|
||||
// We therefore find that f(x, u) = Ax + Bu + [[0] [m * g * r / I *
|
||||
// std::cos(theta)]]
|
||||
// The torque on the arm is given by τ = F⋅r, where F is the force applied by
|
||||
// gravity and r the distance from pivot to center of mass. Recall from
|
||||
// dynamics that the sum of torques for a rigid body is τ = J⋅α, were τ is
|
||||
// torque on the arm, J is the mass-moment of inertia about the pivot axis,
|
||||
// and α is the angular acceleration in rad/s². Rearranging yields: α = F⋅r/J
|
||||
//
|
||||
// We substitute in F = m⋅g⋅cos(θ), where θ is the angle from horizontal:
|
||||
//
|
||||
// α = (m⋅g⋅cos(θ))⋅r/J
|
||||
//
|
||||
// Multiply RHS by cos(θ) to account for the arm angle. Further, we know the
|
||||
// arm mass-moment of inertia J of our arm is given by J=1/3 mL², modeled as a
|
||||
// rod rotating about it's end, where L is the overall rod length. The mass
|
||||
// distribution is assumed to be uniform. Substitute r=L/2 to find:
|
||||
//
|
||||
// α = (m⋅g⋅cos(θ))⋅r/(1/3 mL²)
|
||||
// α = (m⋅g⋅cos(θ))⋅(L/2)/(1/3 mL²)
|
||||
// α = 3/2⋅g⋅cos(θ)/L
|
||||
//
|
||||
// This acceleration is next added to the linear system dynamics ẋ=Ax+Bu
|
||||
//
|
||||
// f(x, u) = Ax + Bu + [0 α]ᵀ
|
||||
// f(x, u) = Ax + Bu + [0 3/2⋅g⋅cos(θ)/L]ᵀ
|
||||
|
||||
Vectord<2> updatedXhat = RKDP(
|
||||
[&](const auto& x, const auto& u) -> Vectord<2> {
|
||||
Vectord<2> xdot = m_plant.A() * x + m_plant.B() * u;
|
||||
|
||||
if (m_simulateGravity) {
|
||||
xdot += Vectord<2>{0.0, (m_armMass * m_r * -9.8 * 3.0 /
|
||||
(m_armMass * m_r * m_r) * std::cos(x(0)))
|
||||
.value()};
|
||||
xdot += Vectord<2>{
|
||||
0.0, (3.0 / 2.0 * -9.8 / m_armLen * std::cos(x(0))).value()};
|
||||
}
|
||||
return xdot;
|
||||
},
|
||||
|
||||
@@ -41,6 +41,7 @@ int frc::RunHALInitialization() {
|
||||
std::puts("FATAL ERROR: HAL could not be initialized");
|
||||
return -1;
|
||||
}
|
||||
DriverStation::RefreshData();
|
||||
HAL_Report(HALUsageReporting::kResourceType_Language,
|
||||
HALUsageReporting::kLanguage_CPlusPlus, 0, GetWPILibVersion());
|
||||
|
||||
|
||||
@@ -227,6 +227,9 @@ class DriverStation final {
|
||||
/**
|
||||
* Returns the game specific message provided by the FMS.
|
||||
*
|
||||
* If the FMS is not connected, it is set from the game data setting on the
|
||||
* driver station.
|
||||
*
|
||||
* @return A string containing the game specific message.
|
||||
*/
|
||||
static std::string GetGameSpecificMessage();
|
||||
@@ -262,7 +265,10 @@ class DriverStation final {
|
||||
static int GetReplayNumber();
|
||||
|
||||
/**
|
||||
* Return the alliance that the driver station says it is on.
|
||||
* Return the alliance that the driver station says it is on from the FMS.
|
||||
*
|
||||
* If the FMS is not connected, it is set from the team alliance setting on
|
||||
* the driver station.
|
||||
*
|
||||
* This could return kRed or kBlue.
|
||||
*
|
||||
@@ -271,7 +277,10 @@ class DriverStation final {
|
||||
static Alliance GetAlliance();
|
||||
|
||||
/**
|
||||
* Return the driver station location on the field.
|
||||
* Return the driver station location from the FMS.
|
||||
*
|
||||
* If the FMS is not connected, it is set from the team alliance setting on
|
||||
* the driver station.
|
||||
*
|
||||
* This could return 1, 2, or 3.
|
||||
*
|
||||
|
||||
@@ -76,6 +76,14 @@ class Timer {
|
||||
*/
|
||||
void Start();
|
||||
|
||||
/**
|
||||
* Restart the timer by stopping the timer, if it is not already stopped,
|
||||
* resetting the accumulated time, then starting the timer again. If you
|
||||
* want an event to periodically reoccur at some time interval from the
|
||||
* start time, consider using AdvanceIfElapsed() instead.
|
||||
*/
|
||||
void Restart();
|
||||
|
||||
/**
|
||||
* Stop the timer.
|
||||
*
|
||||
|
||||
@@ -359,7 +359,7 @@ class XboxController : public GenericHID {
|
||||
* Constructs an event instance around the axis value of the left trigger.
|
||||
* The returned trigger will be true when the axis value is greater than 0.5.
|
||||
* @param loop the event loop instance to attach the event to.
|
||||
* @return an event instance that is true when the right trigger's axis
|
||||
* @return an event instance that is true when the left trigger's axis
|
||||
* exceeds 0.5, attached to the given event loop
|
||||
*/
|
||||
BooleanEvent LeftTrigger(EventLoop* loop) const;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user