Compare commits

...

123 Commits

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

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

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

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

Also add an indexed iterator forEach to loop over the entire buffer.
2024-02-01 14:01:53 -08:00
vichik
90bb6cfffa [wpiunits] Fix measure isNear function (#6313)
Now the function allows comparison between negative numbers, positive numbers or both.
2024-01-31 13:18:47 -08:00
Ryan Blue
cb094e4ff6 [examples] Don't reset encoders when resetting odometry (#6329) 2024-01-31 13:18:07 -08:00
Isaac Turner
60c6ed9812 [ci] Bump python, java and react script versions (#6325) 2024-01-31 13:17:41 -08:00
Tim Winters
ee15cc172a [commands] Reset timer in quasistatic SysIdRoutine test (#6322) 2024-01-27 23:51:00 -08:00
Joseph Eng
1016e95242 [examples] Fix memory leak in C++ controller command examples (#6306) 2024-01-27 23:49:41 -08:00
Sam Carlberg
19f1903959 [wpiunits] Add singularized aliases for built in units (#6323) 2024-01-27 23:47:59 -08:00
DeltaDizzy
53ebb6679e [examples] Move triggers to subsystem fields (#6318) 2024-01-27 23:47:06 -08:00
Tyler Veness
177132fa2a Replace C++ unit .to<double>() with .value() (#6317)
The latter is shorter and is what we use everywhere else.
2024-01-27 07:58:25 -08:00
Thad House
bbb230491a Force cpp files to be LF line endings (#6319)
Just marking a file as text will cause git to override the local core.autocrlf setting, and assume you want it to be true, which isn't what you want.
2024-01-26 23:20:51 -08:00
Tyler Veness
84ef71ace0 [wpimath] Make Rotation2d implicitly convert from any angle unit (#6316)
Add unit category concepts to support this.
2024-01-26 12:49:22 -08:00
Tyler Veness
68736d802d [wpimath] Clean up profile classes (#6311)
* Reorder functions so they match between languages
* Copy more complete JavaDocs to C++
* Fix incorrect description for time parameter of
  TrapezoidProfile.calculate()
2024-01-25 22:22:42 -08:00
Tyler Veness
d895a0c09f [wpiutil] Add std::expected shim (#6310)
Its tests use Catch2 instead of GoogleTest, so we can't import them.
2024-01-25 22:21:37 -08:00
sciencewhiz
64a9d413bf Update contributing for addition of Python (NFC) (#6307) 2024-01-25 21:31:57 -08:00
Chris Gerth
a70e83ae2e [glass] Update field size defaults in Field2D.cpp (#6298)
Looks like the field length is longer in 2024. Used the onshape model to measure the size.
2024-01-23 21:28:17 -08:00
Isaac Turner
47652d7a3c [commands] Remove unused headers (#6300) 2024-01-23 21:27:24 -08:00
Tyler Veness
be78552db7 [sysid] Fix SSTO calculation (#6301) 2024-01-23 21:26:49 -08:00
Peter Johnson
3acae550d6 [ntcore] StructArrayTopic: Publish schema in span-taking setters (#6303) 2024-01-23 21:26:06 -08:00
Thad House
9d55941ce5 [build] Fix macOS apps not always being an application (#6286) 2024-01-21 20:41:08 -08:00
Peter Johnson
51d92c7027 [build] Fix compilation with musl (#6289) 2024-01-21 20:32:56 -08:00
Peter Johnson
9206b47d67 [wpilibc] ADIS16448, ADIS16470: Initialize member pointers (#6282) 2024-01-21 11:49:32 -08:00
Dustin Spicuzza
6fc16264ce [ntcore] NetworkTable: Actually use the I parameter for structs (#6280) 2024-01-21 10:57:11 -08:00
Peter Johnson
ad18f35477 [ntcore] Map int[] to int64[] for DataLog (#6279) 2024-01-21 00:50:27 -08:00
Thad House
0c6bd846bc [hal] Use 64 bit timestamp in DMA (#6278) 2024-01-20 22:34:03 -08:00
Asa Paparo
19c1556472 [commands] Fix SysIdRoutine naming (#6277)
Previously, this used mechanism.m_subsystem.getName(), instead of mechanism.m_name, meaning differently named SysId routines from the same subsystem would clobber each other when logged.
2024-01-20 22:10:19 -08:00
Peter Johnson
3928ed5647 [sim] Sim GUI DS: Add "Disconnected" state and start in it (#6218)
The default state for the DS in the simulated HAL is changed to disconnected.

The FMS view is now only editable in DS disconnected state.

This enables more robot and field-like testing of robot code, as the
alliance color and other parameters start in invalid states and are
only set when the DS connects.
2024-01-20 21:10:02 -08:00
Thad House
e408f3ad27 [rtns] Add functionality to enable and disable webserver (#6270) 2024-01-20 20:15:30 -08:00
Thad House
7957f4a625 [hal] Don't write a 0 length led string to the FPGA (#6271)
Due to something weird in the FPGA, calling strobeLoad() with a string length of 0 causes both CPUs to spin at 100%, basically shutting down everything else on the robot.

If a 0 length string happens to be passed, just bail out early.
2024-01-20 09:32:16 -08:00
Peter Johnson
1241dfdf68 [outlineviewer] Reduce NT log level 2024-01-20 07:24:16 -08:00
Peter Johnson
4d109309c9 [glass] Reduce NT log level 2024-01-20 07:24:16 -08:00
Peter Johnson
7560d18e09 [ntcore] Enable uv/WS debugging 2024-01-20 07:24:16 -08:00
Peter Johnson
f518e143d0 [wpinet] WebSocket: Utilize uv::Handle logging 2024-01-20 07:24:16 -08:00
Peter Johnson
6fab87fa4c [wpinet] uv::Stream: Add logging for Write and TryWrite 2024-01-20 07:24:16 -08:00
Peter Johnson
42c41785ac [wpinet] uv::Handle: Add wpi::Logger support
This allows the uv wrappers and any related classes to log directly to
a wpi::Logger.
2024-01-20 07:24:16 -08:00
Peter Johnson
1a7eeb6282 [ntcore] Enhance WebSocketConnection debug logging 2024-01-20 07:24:16 -08:00
Peter Johnson
98c0827236 [wpinet] WebSocket: serializer bugfixes (#6264) 2024-01-20 00:38:56 -08:00
Peter Johnson
57aa8ca0dd [ci] Unlimit Windows Gradle workers (#6268)
With the GitHub upgrade to 16 GB RAM, this should no longer be needed.
2024-01-19 23:56:00 -08:00
Peter Johnson
789af2ad26 [ntcore] Use last received time instead of last ping response
This relaxes the timeout constraint for long message transmissions.
2024-01-19 23:45:01 -08:00
Peter Johnson
9a5366bb83 [wpiutil] WebSocket: Add GetLastReceivedTime
This allows getting the timestamp that any data has been received.
2024-01-19 23:45:01 -08:00
Tyler Veness
77c09b9ce2 [docs] Build with JavaDoc 17 and add missing docs (#6220)
Co-authored-by: Sam Carlberg <sam.carlberg@gmail.com>
2024-01-19 23:42:09 -08:00
Peter Johnson
9ec27c1202 [ntcore] Don't disconnect with 1005 error code (#6265) 2024-01-19 23:34:25 -08:00
Peter Johnson
d653408873 [ntcore] Fix large text message splitting (#6263)
The written amount wasn't being tracked in the common case, so bulk
announcements after a subscribe would result in a very large WebSocket
frame.
2024-01-19 23:15:25 -08:00
Peter Johnson
24a24c9051 [wpinet] WebSocket: Improve disconnect reason reporting (#6262)
Add "remote close:" to messages coming from the remote end.
Previously it was impossible to tell if the error was on the local side
or communicated by the remote side.
2024-01-19 23:13:38 -08:00
Thad House
0e5eb3f35c [wpiutil] Fix DynamicStruct string handling (#6253)
Dynamic structs had a few major issues.

In C++, if the string was the last definition in the schema, attempting to set a string would trigger an assertion. This has been fixed

Setting a string value could truncate the string actually stored in the struct, if the definition was shorter than the string to set.
There was no way to detect if this case occurred. The set string function now returns a bool if the string was fully written or not.

Reading a string that had a value shorter than the schema definition would result in embedded trailing nulls in the string. This would make comparing string equality basically impossible, as those embedded nulls count for the length of the string.

The above truncating didn't take into account UTF8 code points. This means a truncation could happen in the middle of a unicode character. Depending on the language this had different behavior, but unpaired code points are problematic to detect in any case. On the decoding side, detect if a split UTF8 code point has occurred by the writer, and if so just ignore it and treat it as not part of the string. Doing this on the receive side means a newer receive side is all that is needed to fix this, which is generally a better option then requiring all senders to update.

Actual DynamicStruct instances have 0 units tests for them. Added a bunch of unit tests around strings to ensure things work properly.
2024-01-19 22:24:54 -08:00
Tyler Veness
4b15c73f64 [sysid] Rename motion threshold to velocity threshold to match GUI field name (#6240) 2024-01-19 22:23:51 -08:00
David Vo
a274e297cd Fix trivial errorprone warnings (NFC) (#6135) 2024-01-19 20:46:38 -08:00
David Vo
d198605562 [wpilibc] SysIdRoutineLog: Fix state log entry name typo (#6261)
The Java version has a hyphen between test-state and the mechanism name.
2024-01-19 20:42:57 -08:00
Thad House
dfaad7ca22 [ntcore] Add typed C GetEntryValue and ReadQueueValue functions (#6256) 2024-01-19 20:38:01 -08:00
Tyler Veness
2df82ec957 [sysid] Document using AdvantageScope for troubleshooting (#6247) 2024-01-19 20:37:27 -08:00
Thad House
3661f485af [wpilibj] Don't automatically pull in cscore for all robot projects (#6245) 2024-01-19 20:37:02 -08:00
Thad House
7f9389f101 [wpiutil] DataLog: Remove extra entry parameter from C AddSchema functions (#6246) 2024-01-19 20:35:44 -08:00
Tyler Veness
ca35bcd827 [wpimath] Use tolerance in rotation interpolation tests (#6237) 2024-01-19 20:35:13 -08:00
Tyler Veness
9227d09960 [wpilib] Fix outdated DifferentialDrive docs (#6249)
They accidentally got reverted when undeprecating MotorController in the
review process for #6053.
2024-01-19 20:34:58 -08:00
swirl
370126db38 [build] cmake: add wpinet dependency to cscore-config.cmake (#6242)
Attempting to build with cscore results in the project being unable to find wpinet unless explicitly found with `find_package` earlier.
2024-01-19 20:34:36 -08:00
HarryXChen
1330235918 [sysid] Show warning tooltips next to bad feedforward gains instead of throwing (#6251)
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2024-01-19 20:33:56 -08:00
Jonah
d392570659 [wpilib] SysIdRoutineLog: Defer creation of state log entry (#6259) 2024-01-19 17:43:18 -08:00
Thad House
a2d45dbca4 [wpiutil] DataLog: Add AddSchema functions to C API (#6232) 2024-01-15 23:34:18 -08:00
Isaac Turner
30965b20cf [wpilibc] Use std::atomic in ADIS classes (#6217) 2024-01-15 22:42:19 -08:00
Thad House
5bc942f532 [wpiutil] StructArrayLogEntry: Use the same lock everywhere (#6231) 2024-01-15 22:41:31 -08:00
Tyler Veness
97828bd325 [sysid] Remove unused "gains to encoder counts" checkbox (#6234) 2024-01-15 22:40:45 -08:00
sciencewhiz
6da21c4943 [examples] Fix typo in AprilTag example (NFC) (#6230) 2024-01-14 20:51:52 -08:00
Peter Johnson
ecf1755e3e [glass] Default to 2024 field image (#6225)
Also relax field scale check a bit so 2024 field image passes.
2024-01-14 14:47:54 -08:00
sciencewhiz
154d920e67 [examples] Limit error bit correction in April Tag examples (#6224)
Values >3 are not supported. 64be6ab26a/apriltag.c (L261-L266)
2024-01-13 23:03:58 -08:00
sciencewhiz
d2ee423749 [fieldImages] Use Miklast high resolution field render (#6185)
Image from https://www.chiefdelphi.com/t/2024-crescendo-top-down-field-renders/447764

Imaged cropped to use less space.
2024-01-13 22:28:58 -08:00
Peter Johnson
7e3678b0a4 [glass] Fix Field2d position and scaling (#6222)
Also adds some border padding for the non-image case.
2024-01-13 21:09:02 -08:00
Tyler Veness
4a55d830e4 [wpilibcExamples] Remove redundant initializer (#6212) 2024-01-13 14:09:26 -08:00
Ben Goldberg
420020c0d5 [wpimath] Remove unused include in Quaternion.cpp (#6219) 2024-01-13 08:48:43 -08:00
Eli Barnett
077c8f4092 [sysid] Fix test duration slider responsiveness (#6216) 2024-01-12 23:05:46 -08:00
HarryXChen
84e3a22baa [sysid] Fix peak acceleration filtering behavior in dynamic velocity test (#6207) 2024-01-12 17:05:50 -08:00
Tyler Veness
b482321c0d [commands] Replace SysId hash map with if statements (#6209)
This is much more efficient.
2024-01-12 12:36:59 -08:00
Isaac Turner
d181e353a0 [wpilib] ADIS16470: Add no-param GetAngle and GetRate (#6184)
This helps with backwards compatibility.
2024-01-12 11:00:42 -08:00
Peter Johnson
2386e44f3a [sysid] Filter valid test names (#6200)
Currently the analysis portion only supports quasistatic and dynamic,
forward and reverse. Check for anything not matching and remove it,
along with providing diagnostics of what is being loaded.
2024-01-12 10:58:57 -08:00
Isaac Turner
fa5b604f16 [wpilibc] Remove unused includes (#6208) 2024-01-12 10:58:35 -08:00
Tyler Veness
67e8306819 gitattributes: Mark C++ source files as text (#6210)
Some C++ files had been checked in with CRLF line endings.
This fixes those and also fixes future commits.
2024-01-12 10:53:56 -08:00
Thad House
1981b8debd Fix multiple Java warnings (#6201) 2024-01-12 08:46:21 -08:00
Tyler Veness
ba9c21cf38 [wpilib] Fix SysId log key for acceleration (#6196)
Also add to docs that logging acceleration and current is optional.
2024-01-10 20:48:23 -08:00
Tyler Veness
211c2a375c [build] Run formatter on generate_usage_reporting.py (#6197) 2024-01-10 20:47:54 -08:00
Peter Johnson
75b2fa1cc3 [sysid] Data selector: use timestamps instead of ranges (#6193)
This is somewhat slower, but handles data files that are organized
differently (e.g. entries grouped instead of purely sorted by time).
2024-01-10 20:13:19 -08:00
Chris Gerth
84b089b209 [ntcore] Update alloy-model.adoc (NFC) (#6191)
URL changed
2024-01-10 11:11:35 -08:00
Peter Johnson
ce550705d7 [ntcore] Fix client "received unknown id -1" (#6186)
This was caused by not swallowing id=-1 messages after processing the
first one.
2024-01-09 14:13:05 -08:00
Peter Johnson
3989617bde [ntcore] NetworkTable::GetStruct: Add I template param (#6183) 2024-01-09 12:39:47 -08:00
sciencewhiz
f1836e1321 [fieldImages] Fix 2024 field json (#6179)
Field corners and field size were identical to 2023.
2024-01-08 19:27:55 -08:00
David Vo
d05f179a9a [build] Fix running apriltagsvision Java example (#6173) 2024-01-07 22:51:55 -08:00
sciencewhiz
b1b03bed85 [wpilib] Update MotorControllerGroup deprecation message (#6171)
The current message could be read as encouraging the use of CAN motor
controllers. This tries to make it more clear.
2024-01-07 17:06:26 -08:00
Michael Leong
fa63fbf446 LICENSE.md: Bump year to 2024 (#6169) 2024-01-07 07:17:41 -08:00
351 changed files with 17029 additions and 7611 deletions

3
.gitattributes vendored
View File

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

View File

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

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: React Rocket
uses: actions/github-script@v6
uses: actions/github-script@v7
with:
script: |
const {owner, repo} = context.issue
@@ -34,14 +34,14 @@ jobs:
GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}"
NUMBER: ${{ github.event.issue.number }}
- name: Set up Python 3.8
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Setup Java
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 11
java-version: 17
- name: Install wpiformat
run: pip3 install wpiformat
- name: Run wpiformat

View File

@@ -20,7 +20,7 @@ jobs:
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 13

View File

@@ -70,13 +70,13 @@ jobs:
artifact-name: Win64Debug
architecture: x64
task: "build"
build-options: "-PciDebugOnly --max-workers 1"
build-options: "-PciDebugOnly"
outputs: "build/allOutputs"
build-dir: "c:\\work"
- os: windows-2022
artifact-name: Win64Release
architecture: x64
build-options: "-PciReleaseOnly --max-workers 1"
build-options: "-PciReleaseOnly"
task: "copyAllOutputs"
outputs: "build/allOutputs"
build-dir: "c:\\work"
@@ -84,13 +84,13 @@ jobs:
artifact-name: WinArm64Debug
architecture: x64
task: "build"
build-options: "-PciDebugOnly -Pbuildwinarm64 -Ponlywindowsarm64 --max-workers 1"
build-options: "-PciDebugOnly -Pbuildwinarm64 -Ponlywindowsarm64"
outputs: "build/allOutputs"
build-dir: "c:\\work"
- os: windows-2022
artifact-name: WinArm64Release
architecture: x64
build-options: "-PciReleaseOnly -Pbuildwinarm64 -Ponlywindowsarm64 --max-workers 1"
build-options: "-PciReleaseOnly -Pbuildwinarm64 -Ponlywindowsarm64"
task: "copyAllOutputs"
outputs: "build/allOutputs"
build-dir: "c:\\work"
@@ -112,7 +112,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 17
@@ -171,10 +171,10 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 13
java-version: 17
- name: Set release environment variable
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v')
@@ -230,7 +230,7 @@ jobs:
run: |
cat combiner/products/build/allOutputs/version.txt
test -s combiner/products/build/allOutputs/version.txt
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
if: |
github.repository_owner == 'wpilibsuite' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))

View File

@@ -23,11 +23,11 @@ jobs:
git checkout -b pr
git branch -f main origin/main
- name: Set up Python 3.8
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Install wpiformat
run: pip3 install wpiformat==2023.36
run: pip3 install wpiformat==2024.32
- name: Run
run: wpiformat
- name: Check output
@@ -62,7 +62,7 @@ jobs:
git checkout -b pr
git branch -f main origin/main
- name: Set up Python 3.8
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Install wpiformat
@@ -105,9 +105,9 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 13
java-version: 17
- name: Build with Gradle
run: ./gradlew docs:zipDocs -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }}

View File

@@ -19,7 +19,7 @@ jobs:
with:
fetch-depth: 0
- name: Set up Python 3.9
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Install jinja

View File

@@ -23,7 +23,7 @@ jobs:
git checkout -b pr
git branch -f main origin/main
- name: Set up Python 3.9
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Configure committer identity

View File

@@ -12,11 +12,11 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont
## General Contribution Rules
- Everything in the library must work for the 3000+ teams that will be using it.
- Everything in the library must work for the 4000+ teams that will be using it.
- We need to be able to maintain submitted changes, even if you are no longer working on the project.
- Tool suite changes must be generally useful to a broad range of teams
- Excluding bug fixes, changes in one language generally need to have corresponding changes in other languages.
- Some features, such the addition of C++11 for WPILibC or Functional Interfaces for WPILibJ, are specific to that version of WPILib only.
- Some features, such the addition of C++23 for WPILibC or Functional Interfaces for WPILibJ, are specific to that version of WPILib only. New language features added to C++ must be wrappable in Python for [RobotPy](https://github.com/robotpy).
- Substantial changes often need to have corresponding LabVIEW changes. To do this, we will work with NI on these large changes.
- Changes should have tests.
- Code should be well documented.
@@ -27,7 +27,8 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont
- Bug reports and fixes
- We will generally accept bug fixes without too much question. If they are only implemented for one language, we will implement them for any other necessary languages. Bug reports are also welcome, please submit them to our GitHub issue tracker.
- While we do welcome improvements to the API, there are a few important rules to consider:
- Features must be added to both WPILibC and WPILibJ, with rare exceptions.
- Features must be added to Java (WPILibJ), C++ (WPILibC), with rare exceptions.
- Most of Python (RobotPy) is created by wrapping WPILibC with pybind11 via robotpy-build. However, new features to the command framework should also be submitted to [robotpy-commands-v2](https://github.com/robotpy/robotpy-commands-v2) as the command framework is reimplemented in Python.
- During competition season, we will not merge any new feature additions. We want to ensure that the API is stable during the season to help minimize issues for teams.
- Ask about large changes before spending a bunch of time on them! You can create a new issue on our GitHub tracker for feature request/discussion and talk about it with us there.
- Features that make it easier for teams with less experience to be more successful are more likely to be accepted.

View File

@@ -1,4 +1,4 @@
Copyright (c) 2009-2023 FIRST and other WPILib contributors
Copyright (c) 2009-2024 FIRST and other WPILib contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@@ -1204,3 +1204,8 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================
2024 Field Image
================
2024 Field Image from MikLast: https://www.chiefdelphi.com/t/2024-crescendo-top-down-field-renders/447764

View File

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

View File

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

View File

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

View File

@@ -125,3 +125,37 @@ void frc::from_json(const wpi::json& json, AprilTagFieldLayout& layout) {
layout.m_fieldWidth =
units::meter_t{json.at("field").at("width").get<double>()};
}
// Use namespace declaration for forward declaration
namespace frc {
// C++ generated from resource files
std::string_view GetResource_2022_rapidreact_json();
std::string_view GetResource_2023_chargedup_json();
std::string_view GetResource_2024_crescendo_json();
} // namespace frc
AprilTagFieldLayout AprilTagFieldLayout::LoadField(AprilTagField field) {
std::string_view fieldString;
switch (field) {
case AprilTagField::k2022RapidReact:
fieldString = GetResource_2022_rapidreact_json();
break;
case AprilTagField::k2023ChargedUp:
fieldString = GetResource_2023_chargedup_json();
break;
case AprilTagField::k2024Crescendo:
fieldString = GetResource_2024_crescendo_json();
break;
case AprilTagField::kNumFields:
throw std::invalid_argument("Invalid Field");
}
wpi::json json = wpi::json::parse(fieldString);
return json.get<AprilTagFieldLayout>();
}
AprilTagFieldLayout frc::LoadAprilTagLayoutField(AprilTagField field) {
return AprilTagFieldLayout::LoadField(field);
}

View File

@@ -1,36 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "frc/apriltag/AprilTagFields.h"
#include <wpi/json.h>
namespace frc {
// C++ generated from resource files
std::string_view GetResource_2022_rapidreact_json();
std::string_view GetResource_2023_chargedup_json();
std::string_view GetResource_2024_crescendo_json();
AprilTagFieldLayout LoadAprilTagLayoutField(AprilTagField field) {
std::string_view fieldString;
switch (field) {
case AprilTagField::k2022RapidReact:
fieldString = GetResource_2022_rapidreact_json();
break;
case AprilTagField::k2023ChargedUp:
fieldString = GetResource_2023_chargedup_json();
break;
case AprilTagField::k2024Crescendo:
fieldString = GetResource_2024_crescendo_json();
break;
case AprilTagField::kNumFields:
throw std::invalid_argument("Invalid Field");
}
wpi::json json = wpi::json::parse(fieldString);
return json.get<AprilTagFieldLayout>();
}
} // namespace frc

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -27,6 +27,7 @@ import edu.wpi.first.networktables.StringArrayTopic;
import edu.wpi.first.networktables.StringEntry;
import edu.wpi.first.networktables.StringPublisher;
import edu.wpi.first.util.PixelFormat;
import java.lang.ref.Reference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -140,6 +141,7 @@ public final class CameraServer {
if (m_choicesPublisher != null) {
m_choicesPublisher.close();
}
Reference.reachabilityFence(m_videoListener);
}
BooleanEntry m_booleanValueEntry;
@@ -223,7 +225,7 @@ public final class CameraServer {
// - "PropertyInfo/{Property}" - Property supporting information
// Listener for video events
@SuppressWarnings({"PMD.UnusedPrivateField", "PMD.AvoidCatchingGenericException"})
@SuppressWarnings("PMD.AvoidCatchingGenericException")
private static final VideoListener m_videoListener =
new VideoListener(
event -> {

View File

@@ -1,6 +1,7 @@
include(CMakeFindDependencyMacro)
@FILENAME_DEP_REPLACE@
@WPIUTIL_DEP_REPLACE@
@WPINET_DEP_REPLACE@
find_dependency(OpenCV)
@FILENAME_DEP_REPLACE@

View File

@@ -74,18 +74,62 @@ public class CameraServerCvJNI {
libraryLoaded = true;
}
/**
* Creates a CV source.
*
* @param name Name.
* @param pixelFormat OpenCV pixel format.
* @param width Image width.
* @param height Image height.
* @param fps Frames per second.
* @return CV source.
*/
public static native int createCvSource(
String name, int pixelFormat, int width, int height, int fps);
/**
* Put source frame.
*
* @param source Source handle.
* @param imageNativeObj Image native object handle.
*/
public static native void putSourceFrame(int source, long imageNativeObj);
/**
* Creates a CV sink.
*
* @param name Name.
* @param pixelFormat OpenCV pixel format.
* @return CV sink handle.
*/
public static native int createCvSink(String name, int pixelFormat);
// /**
// * Creates a CV sink callback.
// *
// * @param name Name.
// * @param processFrame Process frame callback.
// */
// public static native int createCvSinkCallback(String name,
// void (*processFrame)(long time));
/**
* Returns sink frame handle.
*
* @param sink Sink handle.
* @param imageNativeObj Image native object handle.
* @return Sink frame handle.
*/
public static native long grabSinkFrame(int sink, long imageNativeObj);
/**
* Returns sink frame timeout in microseconds.
*
* @param sink Sink handle.
* @param imageNativeObj Image native object handle.
* @param timeout Timeout in seconds.
* @return Sink frame timeout in microseconds.
*/
public static native long grabSinkFrameTimeout(int sink, long imageNativeObj, double timeout);
/** Utility class. */

View File

@@ -77,140 +77,521 @@ public class CameraServerJNI {
//
// Property Functions
//
/**
* Returns property kind.
*
* @param property Property handle.
* @return Property kind.
*/
public static native int getPropertyKind(int property);
/**
* Returns property name.
*
* @param property Property handle.
* @return Property name.
*/
public static native String getPropertyName(int property);
/**
* Returns property value.
*
* @param property Property handle.
* @return Property value.
*/
public static native int getProperty(int property);
/**
* Sets property value.
*
* @param property Property handle.
* @param value Property value.
*/
public static native void setProperty(int property, int value);
/**
* Returns property minimum.
*
* @param property Property handle.
* @return Property minimum.
*/
public static native int getPropertyMin(int property);
/**
* Returns property maximum.
*
* @param property Property handle.
* @return Property maximum.
*/
public static native int getPropertyMax(int property);
/**
* Returns property step.
*
* @param property Property handle.
* @return Property step.
*/
public static native int getPropertyStep(int property);
/**
* Returns property default value.
*
* @param property Property handle.
* @return Property default value.
*/
public static native int getPropertyDefault(int property);
/**
* Returns property value as a string.
*
* @param property Property handle.
* @return Property value as a string.
*/
public static native String getStringProperty(int property);
/**
* Sets property value to a string.
*
* @param property Property handle.
* @param value Property value string.
*/
public static native void setStringProperty(int property, String value);
/**
* Returns enum of possible property value strings.
*
* @param property Property handle.
* @return Enum of possible property value strings.
*/
public static native String[] getEnumPropertyChoices(int property);
//
// Source Creation Functions
//
/**
* Creates a new USB camera by device.
*
* @param name USB camera name.
* @param dev USB camera device number.
* @return USB camera handle.
*/
public static native int createUsbCameraDev(String name, int dev);
/**
* Creates a new USB camera by path.
*
* @param name USB camera name.
* @param path USB camera path.
* @return USB camera handle.
*/
public static native int createUsbCameraPath(String name, String path);
/**
* Creates an HTTP camera.
*
* @param name HTTP camera name.
* @param url HTTP camera stream URL.
* @param kind HTTP camera kind.
* @return HTTP camera handle.
*/
public static native int createHttpCamera(String name, String url, int kind);
/**
* Creates an HTTP camera from multiple URLs.
*
* @param name HTTP camera name.
* @param urls HTTP camera stream URLs.
* @param kind HTTP camera kind.
* @return HTTP camera handle.
*/
public static native int createHttpCameraMulti(String name, String[] urls, int kind);
/**
* Creates a raw source.
*
* @param name Source name.
* @param pixelFormat Pixel format.
* @param width Image width.
* @param height Image height.
* @param fps Source frames per second.
* @return Raw source handle.
*/
public static native int createRawSource(
String name, int pixelFormat, int width, int height, int fps);
//
// Source Functions
//
/**
* Returns source kind.
*
* @param source Source handle.
* @return Source kind.
*/
public static native int getSourceKind(int source);
/**
* Returns source name.
*
* @param source Source handle.
* @return Source name.
*/
public static native String getSourceName(int source);
/**
* Returns source description.
*
* @param source Source handle.
* @return Source description.
*/
public static native String getSourceDescription(int source);
/**
* Returns source's last frame time.
*
* @param source Source handle.
* @return Source's last frame time.
*/
public static native long getSourceLastFrameTime(int source);
/**
* Sets source connection strategy.
*
* @param source Source handle.
* @param strategy Connection strategy.
*/
public static native void setSourceConnectionStrategy(int source, int strategy);
/**
* Returns true if source is connected.
*
* @param source Source handle.
* @return True if source is connected.
*/
public static native boolean isSourceConnected(int source);
/**
* Returns true if source is enabled.
*
* @param source Source handle.
* @return True if source is enabled.
*/
public static native boolean isSourceEnabled(int source);
/**
* Returns source property.
*
* @param source Source handle.
* @param name Source property name.
* @return Source property.
*/
public static native int getSourceProperty(int source, String name);
/**
* Returns list of source property handles.
*
* @param source Source handle.
* @return List of source property handles.
*/
public static native int[] enumerateSourceProperties(int source);
/**
* Returns source video mode.
*
* @param source Source handle.
* @return Source video mode.
*/
public static native VideoMode getSourceVideoMode(int source);
/**
* Sets source video mode.
*
* @param source Source handle.
* @param pixelFormat Pixel format.
* @param width Image width.
* @param height Image height.
* @param fps Source frames per second.
* @return True if set succeeded.
*/
public static native boolean setSourceVideoMode(
int source, int pixelFormat, int width, int height, int fps);
/**
* Sets source pixel format.
*
* @param source Source handle.
* @param pixelFormat Source pixel format.
* @return True if set succeeded.
*/
public static native boolean setSourcePixelFormat(int source, int pixelFormat);
/**
* Sets source resolution.
*
* @param source Source handle.
* @param width Image width.
* @param height Image height.
* @return True if set succeeded.
*/
public static native boolean setSourceResolution(int source, int width, int height);
/**
* Sets source FPS.
*
* @param source Source handle.
* @param fps Source frames per second.
* @return True if set succeeded.
*/
public static native boolean setSourceFPS(int source, int fps);
/**
* Sets source configuration JSON.
*
* @param source Source handle.
* @param config Configuration JSON.
* @return True if set succeeded.
*/
public static native boolean setSourceConfigJson(int source, String config);
/**
* Returns source configuration JSON.
*
* @param source Source handle.
* @return Source configuration JSON.
*/
public static native String getSourceConfigJson(int source);
/**
* Returns list of source's supported video modes.
*
* @param source Source handle.
* @return List of source's supported video modes.
*/
public static native VideoMode[] enumerateSourceVideoModes(int source);
/**
* Returns list of source sinks.
*
* @param source Source handle.
* @return List of source sinks.
*/
public static native int[] enumerateSourceSinks(int source);
/**
* Copies source.
*
* @param source Source handle.
* @return New source handle.
*/
public static native int copySource(int source);
/**
* Releases source.
*
* @param source Source handle.
*/
public static native void releaseSource(int source);
//
// Camera Source Common Property Functions
//
/**
* Sets camera brightness.
*
* @param source Source handle.
* @param brightness Brightness.
*/
public static native void setCameraBrightness(int source, int brightness);
/**
* Returns camera brightness.
*
* @param source Source handle.
* @return Camera brightness.
*/
public static native int getCameraBrightness(int source);
/**
* Sets camera white balance to auto.
*
* @param source Source handle.
*/
public static native void setCameraWhiteBalanceAuto(int source);
/**
* Sets camera white balance to "hold current".
*
* @param source Source handle.
*/
public static native void setCameraWhiteBalanceHoldCurrent(int source);
/**
* Sets camera white balance to the given value.
*
* @param source Source handle.
* @param value White balance.
*/
public static native void setCameraWhiteBalanceManual(int source, int value);
/**
* Sets camera exposure to auto.
*
* @param source Source handle.
*/
public static native void setCameraExposureAuto(int source);
/**
* Sets camera exposure to "hold current".
*
* @param source Source handle.
*/
public static native void setCameraExposureHoldCurrent(int source);
/**
* Sets camera exposure to the given value.
*
* @param source Source handle.
* @param value Exposure.
*/
public static native void setCameraExposureManual(int source, int value);
//
// UsbCamera Source Functions
//
/**
* Sets USB camera path.
*
* @param source Source handle.
* @param path USB camera path.
*/
public static native void setUsbCameraPath(int source, String path);
/**
* Returns USB camera path.
*
* @param source Source handle.
* @return USB camera path.
*/
public static native String getUsbCameraPath(int source);
/**
* Returns USB camera info.
*
* @param source Source handle.
* @return USB camera info.
*/
public static native UsbCameraInfo getUsbCameraInfo(int source);
//
// HttpCamera Source Functions
//
/**
* Returns HTTP camera kind.
*
* @param source Source handle.
* @return HTTP camera kind.
*/
public static native int getHttpCameraKind(int source);
/**
* Sets HTTP camera URLs.
*
* @param source Source handle.
* @param urls HTTP camera URLs.
*/
public static native void setHttpCameraUrls(int source, String[] urls);
/**
* Returns HTTP camera URLs.
*
* @param source Source handle.
* @return HTTP camera URLs.
*/
public static native String[] getHttpCameraUrls(int source);
//
// Image Source Functions
//
/**
* Puts raw frame into source.
*
* @param source Source handle.
* @param frame Frame handle.
*/
public static native void putRawSourceFrame(int source, long frame);
/**
* Puts raw frame into source.
*
* @param source Source handle.
* @param data Frame byte buffer.
* @param size Frame size.
* @param width Frame width.
* @param height Frame height.
* @param stride Frame stride.
* @param pixelFormat Frame pixel format.
*/
public static native void putRawSourceFrameBB(
int source, ByteBuffer data, int size, int width, int height, int stride, int pixelFormat);
/**
* Puts raw frame into source.
*
* @param source Source handle.
* @param data Frame handle.
* @param size Frame size.
* @param width Frame width.
* @param height Frame height.
* @param stride Frame stride.
* @param pixelFormat Frame pixel format.
*/
public static native void putRawSourceFrameData(
int source, long data, int size, int width, int height, int stride, int pixelFormat);
/**
* Notify source error.
*
* @param source Source handle.
* @param msg Error message.
*/
public static native void notifySourceError(int source, String msg);
/**
* Sets whether source is connected.
*
* @param source Source handle.
* @param connected True if source is connected.
*/
public static native void setSourceConnected(int source, boolean connected);
/**
* Sets source description.
*
* @param source Source handle.
* @param description Source description.
*/
public static native void setSourceDescription(int source, String description);
/**
* Creates a source property.
*
* @param source Source handle.
* @param name Property name.
* @param kind Property kind.
* @param minimum Property minimum.
* @param maximum Property maximum.
* @param step Property step.
* @param defaultValue Property default value.
* @param value Property value.
* @return Source property handle.
*/
public static native int createSourceProperty(
int source,
String name,
@@ -221,88 +602,288 @@ public class CameraServerJNI {
int defaultValue,
int value);
/**
* Sets list of possible property values.
*
* @param source Source handle.
* @param property Property handle.
* @param choices List of possible property values.
*/
public static native void setSourceEnumPropertyChoices(
int source, int property, String[] choices);
//
// Sink Creation Functions
//
/**
* Creates an MJPEG server.
*
* @param name MJPEG server name.
* @param listenAddress IP address at which server should listen.
* @param port Port on which server should listen.
* @return MJPEG server handle.
*/
public static native int createMjpegServer(String name, String listenAddress, int port);
/**
* Creates a raw sink.
*
* @param name Sink name.
* @return Raw sink handle.
*/
public static native int createRawSink(String name);
//
// Sink Functions
//
/**
* Returns sink kind.
*
* @param sink Sink handle.
* @return Sink kind.
*/
public static native int getSinkKind(int sink);
/**
* Returns sink name.
*
* @param sink Sink handle.
* @return Sink name.
*/
public static native String getSinkName(int sink);
/**
* Returns sink description.
*
* @param sink Sink handle.
* @return Sink description.
*/
public static native String getSinkDescription(int sink);
/**
* Returns sink property.
*
* @param sink Sink handle.
* @param name Property name.
* @return Sink property handle.
*/
public static native int getSinkProperty(int sink, String name);
/**
* Returns list of sink property handles.
*
* @param sink Sink handle.
* @return List of sink property handles.
*/
public static native int[] enumerateSinkProperties(int sink);
/**
* Sets sink configuration JSON.
*
* @param sink Sink handle.
* @param config Configuration JSON.
* @return True if set succeeded.
*/
public static native boolean setSinkConfigJson(int sink, String config);
/**
* Returns sink configuration JSON.
*
* @param sink Sink handle.
* @return Sink configuration JSON.
*/
public static native String getSinkConfigJson(int sink);
/**
* Sets sink source.
*
* @param sink Sink handle.
* @param source Source handle.
*/
public static native void setSinkSource(int sink, int source);
/**
* Returns sink source property.
*
* @param sink Sink handle.
* @param name Property name.
* @return Sink source property handle.
*/
public static native int getSinkSourceProperty(int sink, String name);
/**
* Returns sink source.
*
* @param sink Sink handle.
* @return Sink source handle.
*/
public static native int getSinkSource(int sink);
/**
* Copies sink.
*
* @param sink Sink handle.
* @return New sink handle.
*/
public static native int copySink(int sink);
/**
* Releases sink.
*
* @param sink Sink handle.
*/
public static native void releaseSink(int sink);
//
// MjpegServer Sink Functions
//
/**
* Returns MJPEG server listen address.
*
* @param sink Sink handle.
* @return MJPEG server listen address.
*/
public static native String getMjpegServerListenAddress(int sink);
/**
* Returns MJPEG server port.
*
* @param sink Sink handle.
* @return MJPEG server port.
*/
public static native int getMjpegServerPort(int sink);
//
// Image Sink Functions
//
/**
* Sets sink description.
*
* @param sink Sink handle.
* @param description Sink description.
*/
public static native void setSinkDescription(int sink, String description);
/**
* Returns raw sink frame.
*
* @param sink Sink handle.
* @param frame Raw frame.
* @param nativeObj Native object.
* @return Raw sink frame handle.
*/
public static native long grabRawSinkFrame(int sink, RawFrame frame, long nativeObj);
/**
* Returns raw sink frame timeout.
*
* @param sink Sink handle.
* @param frame Raw frame.
* @param nativeObj Native object.
* @param timeout Timeout in seconds.
* @return Raw sink frame timeout.
*/
public static native long grabRawSinkFrameTimeout(
int sink, RawFrame frame, long nativeObj, double timeout);
/**
* Returns sink error message.
*
* @param sink Sink handle.
* @return Sink error message.
*/
public static native String getSinkError(int sink);
/**
* Sets sink enable.
*
* @param sink Sink handle.
* @param enabled True if sink should be enabled.
*/
public static native void setSinkEnabled(int sink, boolean enabled);
//
// Listener Functions
//
/**
* Adds listener.
*
* @param listener Video event callback.
* @param eventMask Event mask.
* @param immediateNotify True to immediately notify on event.
* @return Listener handle.
*/
public static native int addListener(
Consumer<VideoEvent> listener, int eventMask, boolean immediateNotify);
/**
* Removes listener.
*
* @param handle Listener handle.
*/
public static native void removeListener(int handle);
/**
* Creates listener poller.
*
* @return Listener poller handle.
*/
public static native int createListenerPoller();
/**
* Destroys listener poller.
*
* @param poller Listener poller handle.
*/
public static native void destroyListenerPoller(int poller);
/**
* Add polled listener.
*
* @param poller Poller handle.
* @param eventMask Event mask.
* @param immediateNotify True to immediately notify on event.
* @return Polled listener handle.
*/
public static native int addPolledListener(int poller, int eventMask, boolean immediateNotify);
/**
* Polls listener.
*
* @param poller Poller handle.
* @return List of video events.
* @throws InterruptedException if polling was interrupted.
*/
public static native VideoEvent[] pollListener(int poller) throws InterruptedException;
/**
* Polls listener with timeout.
*
* @param poller Poller handle.
* @param timeout Timeout in seconds.
* @return List of video events.
* @throws InterruptedException if polling was interrupted.
*/
public static native VideoEvent[] pollListenerTimeout(int poller, double timeout)
throws InterruptedException;
/**
* Cancels poll listener.
*
* @param poller Poller handle.
*/
public static native void cancelPollListener(int poller);
//
// Telemetry Functions
//
/** Telemetry kind. */
public enum TelemetryKind {
/** kSourceBytesReceived. */
kSourceBytesReceived(1),
@@ -315,23 +896,66 @@ public class CameraServerJNI {
this.value = value;
}
/**
* Returns telemetry kind value.
*
* @return Telemetry kind value.
*/
public int getValue() {
return value;
}
}
/**
* Sets telemetry period.
*
* @param seconds Telemetry period in seconds.
*/
public static native void setTelemetryPeriod(double seconds);
/**
* Returns telemetry elapsed time.
*
* @return Telemetry elapsed time.
*/
public static native double getTelemetryElapsedTime();
/**
* Returns telemetry value.
*
* @param handle Telemetry handle.
* @param kind Telemetry kind.
* @return Telemetry value.
*/
public static native long getTelemetryValue(int handle, int kind);
/**
* Returns telemetry value.
*
* @param handle Telemetry handle.
* @param kind Telemetry kind.
* @return Telemetry value.
*/
public static long getTelemetryValue(int handle, TelemetryKind kind) {
return getTelemetryValue(handle, kind.getValue());
}
/**
* Returns telemetry average value.
*
* @param handle Telemetry handle.
* @param kind Telemetry kind.
* @return Telemetry average value.
*/
public static native double getTelemetryAverageValue(int handle, int kind);
/**
* Returns telemetry average value.
*
* @param handle Telemetry handle.
* @param kind Telemetry kind.
* @return Telemetry average value.
*/
public static double getTelemetryAverageValue(int handle, TelemetryKind kind) {
return getTelemetryAverageValue(handle, kind.getValue());
}
@@ -339,30 +963,80 @@ public class CameraServerJNI {
//
// Logging Functions
//
/** Logger functional interface. */
@FunctionalInterface
public interface LoggerFunction {
/**
* Log a string.
*
* @param level Log level.
* @param file Log file.
* @param line Line number.
* @param msg Log message.
*/
void apply(int level, String file, int line, String msg);
}
/**
* Sets logger.
*
* @param func Logger function.
* @param minLevel Minimum logging level.
*/
public static native void setLogger(LoggerFunction func, int minLevel);
//
// Utility Functions
//
/**
* Returns list of USB cameras.
*
* @return List of USB cameras.
*/
public static native UsbCameraInfo[] enumerateUsbCameras();
/**
* Returns list of sources.
*
* @return List of sources.
*/
public static native int[] enumerateSources();
/**
* Returns list of sinks.
*
* @return List of sinks.
*/
public static native int[] enumerateSinks();
/**
* Returns hostname.
*
* @return Hostname.
*/
public static native String getHostname();
/**
* Returns list of network interfaces.
*
* @return List of network interfaces.
*/
public static native String[] getNetworkInterfaces();
/** Runs main run loop. */
public static native void runMainRunLoop();
/**
* Runs main run loop with timeout.
*
* @param timeoutSeconds Timeout in seconds.
* @return 3 on timeout, 2 on signal, 1 on other.
*/
public static native int runMainRunLoopTimeout(double timeoutSeconds);
/** Stops main run loop. */
public static native void stopMainRunLoop();
/** Utility class. */

View File

@@ -6,6 +6,7 @@ package edu.wpi.first.cscore;
/** A source that represents a MJPEG-over-HTTP (IP) camera. */
public class HttpCamera extends VideoCamera {
/** HTTP camera kind. */
public enum HttpCameraKind {
/** Unknown camera kind. */
kUnknown(0),
@@ -22,6 +23,11 @@ public class HttpCamera extends VideoCamera {
this.value = value;
}
/**
* Returns HttpCameraKind value.
*
* @return HttpCameraKind value.
*/
public int getValue() {
return value;
}

View File

@@ -4,7 +4,13 @@
package edu.wpi.first.cscore;
/** A base class for single image reading sinks. */
public abstract class ImageSink extends VideoSink {
/**
* Constructs an ImageSink.
*
* @param handle The image sink handle.
*/
protected ImageSink(int handle) {
super(handle);
}

View File

@@ -4,7 +4,13 @@
package edu.wpi.first.cscore;
/** A base class for single image providing sources. */
public abstract class ImageSource extends VideoSource {
/**
* Constructs an ImageSource.
*
* @param handle The image source handle.
*/
protected ImageSource(int handle) {
super(handle);
}

View File

@@ -6,17 +6,32 @@ package edu.wpi.first.cscore;
/** A source that represents a video camera. */
public class VideoCamera extends VideoSource {
/** White balance. */
public static class WhiteBalance {
/** Fixed indoor white balance. */
public static final int kFixedIndoor = 3000;
/** Fixed outdoor white balance 1. */
public static final int kFixedOutdoor1 = 4000;
/** Fixed outdoor white balance 2. */
public static final int kFixedOutdoor2 = 5000;
/** Fixed fluorescent white balance 1. */
public static final int kFixedFluorescent1 = 5100;
/** Fixed fluorescent white balance 2. */
public static final int kFixedFlourescent2 = 5200;
/** Default constructor. */
public WhiteBalance() {}
}
/**
* Constructs a VideoCamera.
*
* @param handle The video camera handle.
*/
protected VideoCamera(int handle) {
super(handle);
}

View File

@@ -7,6 +7,7 @@ package edu.wpi.first.cscore;
/** Video event. */
@SuppressWarnings("MemberName")
public class VideoEvent {
/** VideoEvent kind. */
public enum Kind {
/** Unknown video event. */
kUnknown(0x0000),
@@ -57,6 +58,11 @@ public class VideoEvent {
this.value = value;
}
/**
* Returns the kind value.
*
* @return The kind value.
*/
public int getValue() {
return value;
}
@@ -139,39 +145,67 @@ public class VideoEvent {
this.listener = listener;
}
/** The video event kind. */
public Kind kind;
// Valid for kSource* and kSink* respectively
/**
* The source handle.
*
* <p>Valid for kSource* and kSink* respectively.
*/
public int sourceHandle;
/** The sink handle. */
public int sinkHandle;
// Source/sink/property name
/** Source/sink/property name. */
public String name;
// Fields for kSourceVideoModeChanged event
// Fields for kSourceVideoModeChanged event.
/** New source video mode. */
public VideoMode mode;
// Fields for kSourceProperty* events
// Fields for kSourceProperty* events.
/** Source property handle. */
public int propertyHandle;
/** Source property kind. */
public VideoProperty.Kind propertyKind;
/** Event value. */
public int value;
/** Event value as a string. */
public String valueStr;
// Listener that was triggered
/** Listener that was triggered. */
public int listener;
/**
* Returns the source associated with the event (if any).
*
* @return The source associated with the event (if any).
*/
public VideoSource getSource() {
return new VideoSource(CameraServerJNI.copySource(sourceHandle));
}
/**
* Returns the sink associated with the event (if any).
*
* @return The sink associated with the event (if any).
*/
public VideoSink getSink() {
return new VideoSink(CameraServerJNI.copySink(sinkHandle));
}
/**
* Returns the property associated with the event (if any).
*
* @return The property associated with the event (if any).
*/
public VideoProperty getProperty() {
return new VideoProperty(propertyHandle, propertyKind);
}

View File

@@ -8,6 +8,11 @@ package edu.wpi.first.cscore;
public class VideoException extends RuntimeException {
private static final long serialVersionUID = -9155939328084105145L;
/**
* Constructs the exception with the given message.
*
* @param msg The exception message.
*/
public VideoException(String msg) {
super(msg);
}

View File

@@ -51,6 +51,11 @@ public class VideoListener implements AutoCloseable {
}
}
/**
* Returns true if the video listener handle is valid.
*
* @return True if the video listener handle is valid.
*/
public boolean isValid() {
return m_handle != 0;
}

View File

@@ -25,6 +25,11 @@ public class VideoProperty {
this.value = value;
}
/**
* Returns the Kind value.
*
* @return The Kind value.
*/
public int getValue() {
return value;
}
@@ -51,69 +56,152 @@ public class VideoProperty {
}
}
/**
* Returns property name.
*
* @return Property name.
*/
public String getName() {
return CameraServerJNI.getPropertyName(m_handle);
}
/**
* Returns property kind.
*
* @return Property kind.
*/
public Kind getKind() {
return m_kind;
}
/**
* Returns true if property is valid.
*
* @return True if property is valid.
*/
public boolean isValid() {
return m_kind != Kind.kNone;
}
// Kind checkers
/**
* Returns true if property is a boolean.
*
* @return True if property is a boolean.
*/
public boolean isBoolean() {
return m_kind == Kind.kBoolean;
}
/**
* Returns true if property is an integer.
*
* @return True if property is an integer.
*/
public boolean isInteger() {
return m_kind == Kind.kInteger;
}
/**
* Returns true if property is a string.
*
* @return True if property is a string.
*/
public boolean isString() {
return m_kind == Kind.kString;
}
/**
* Returns true if property is an enum.
*
* @return True if property is an enum.
*/
public boolean isEnum() {
return m_kind == Kind.kEnum;
}
/**
* Returns property value.
*
* @return Property value.
*/
public int get() {
return CameraServerJNI.getProperty(m_handle);
}
/**
* Sets property value.
*
* @param value Property value.
*/
public void set(int value) {
CameraServerJNI.setProperty(m_handle, value);
}
/**
* Returns property minimum value.
*
* @return Property minimum value.
*/
public int getMin() {
return CameraServerJNI.getPropertyMin(m_handle);
}
/**
* Returns property maximum value.
*
* @return Property maximum value.
*/
public int getMax() {
return CameraServerJNI.getPropertyMax(m_handle);
}
/**
* Returns property step size.
*
* @return Property step size.
*/
public int getStep() {
return CameraServerJNI.getPropertyStep(m_handle);
}
/**
* Returns property default value.
*
* @return Property default value.
*/
public int getDefault() {
return CameraServerJNI.getPropertyDefault(m_handle);
}
// String-specific functions
/**
* Returns the string property value.
*
* <p>This function is string-specific.
*
* @return The string property value.
*/
public String getString() {
return CameraServerJNI.getStringProperty(m_handle);
}
/**
* Sets the string property value.
*
* <p>This function is string-specific.
*
* @param value String property value.
*/
public void setString(String value) {
CameraServerJNI.setStringProperty(m_handle, value);
}
// Enum-specific functions
/**
* Returns the possible values for the enum property value.
*
* <p>This function is enum-specific.
*
* @return The possible values for the enum property value.
*/
public String[] getChoices() {
return CameraServerJNI.getEnumPropertyChoices(m_handle);
}

View File

@@ -26,6 +26,11 @@ public class VideoSink implements AutoCloseable {
this.value = value;
}
/**
* Returns the Kind value.
*
* @return The Kind value.
*/
public int getValue() {
return value;
}
@@ -48,6 +53,11 @@ public class VideoSink implements AutoCloseable {
}
}
/**
* Constructs a VideoSink.
*
* @param handle The video sink handle.
*/
protected VideoSink(int handle) {
m_handle = handle;
}
@@ -60,10 +70,20 @@ public class VideoSink implements AutoCloseable {
m_handle = 0;
}
/**
* Returns true if the VideoSink is valid.
*
* @return True if the VideoSink is valid.
*/
public boolean isValid() {
return m_handle != 0;
}
/**
* Returns the video sink handle.
*
* @return The video sink handle.
*/
public int getHandle() {
return m_handle;
}
@@ -222,5 +242,6 @@ public class VideoSink implements AutoCloseable {
return rv;
}
/** The VideoSink handle. */
protected int m_handle;
}

View File

@@ -11,6 +11,7 @@ import edu.wpi.first.util.PixelFormat;
* (e.g. from a stereo or depth camera); these are called channels.
*/
public class VideoSource implements AutoCloseable {
/** Video source kind. */
public enum Kind {
/** Unknown video source. */
kUnknown(0),
@@ -29,6 +30,11 @@ public class VideoSource implements AutoCloseable {
this.value = value;
}
/**
* Returns the Kind value.
*
* @return The Kind value.
*/
public int getValue() {
return value;
}
@@ -56,6 +62,11 @@ public class VideoSource implements AutoCloseable {
this.value = value;
}
/**
* Returns the ConnectionStrategy value.
*
* @return The ConnectionStrategy value.
*/
public int getValue() {
return value;
}
@@ -80,6 +91,11 @@ public class VideoSource implements AutoCloseable {
}
}
/**
* Constructs a VideoSource.
*
* @param handle The video source handle.
*/
protected VideoSource(int handle) {
m_handle = handle;
}
@@ -92,10 +108,20 @@ public class VideoSource implements AutoCloseable {
m_handle = 0;
}
/**
* Returns true if the VideoSource is valid.
*
* @return True if the VideoSource is valid.
*/
public boolean isValid() {
return m_handle != 0;
}
/**
* Returns the video source handle.
*
* @return The video source handle.
*/
public int getHandle() {
return m_handle;
}
@@ -379,5 +405,6 @@ public class VideoSource implements AutoCloseable {
return rv;
}
/** Video source handle. */
protected int m_handle;
}

View File

@@ -58,33 +58,139 @@ class VideoProperty {
VideoProperty() = default;
/**
* Returns property name.
*
* @return Property name.
*/
std::string GetName() const;
/**
* Returns property kind.
*
* @return Property kind.
*/
Kind GetKind() const { return m_kind; }
/**
* Returns true if property is valid.
*
* @return True if property is valid.
*/
explicit operator bool() const { return m_kind != kNone; }
// Kind checkers
/**
* Returns true if property is a boolean.
*
* @return True if property is a boolean.
*/
bool IsBoolean() const { return m_kind == kBoolean; }
/**
* Returns true if property is an integer.
*
* @return True if property is an integer.
*/
bool IsInteger() const { return m_kind == kInteger; }
/**
* Returns true if property is a string.
*
* @return True if property is a string.
*/
bool IsString() const { return m_kind == kString; }
/**
* Returns true if property is an enum.
*
* @return True if property is an enum.
*/
bool IsEnum() const { return m_kind == kEnum; }
/**
* Returns property value.
*
* @return Property value.
*/
int Get() const;
/**
* Sets property value.
*
* @param value Property value.
*/
void Set(int value);
/**
* Returns property minimum value.
*
* @return Property minimum value.
*/
int GetMin() const;
/**
* Returns property maximum value.
*
* @return Property maximum value.
*/
int GetMax() const;
/**
* Returns property step size.
*
* @return Property step size.
*/
int GetStep() const;
/**
* Returns property default value.
*
* @return Property default value.
*/
int GetDefault() const;
// String-specific functions
/**
* Returns the string property value.
*
* <p>This function is string-specific.
*
* @return The string property value.
*/
std::string GetString() const;
/**
* Returns the string property value as a reference to the given buffer.
*
* This function is string-specific.
*
* @param buf The backing storage to which to write the property value.
* @return The string property value as a reference to the given buffer.
*/
std::string_view GetString(wpi::SmallVectorImpl<char>& buf) const;
/**
* Sets the string property value.
*
* This function is string-specific.
*
* @param value String property value.
*/
void SetString(std::string_view value);
// Enum-specific functions
/**
* Returns the possible values for the enum property value.
*
* This function is enum-specific.
*
* @return The possible values for the enum property value.
*/
std::vector<std::string> GetChoices() const;
/**
* Returns the last status.
*
* @return The last status.
*/
CS_Status GetLastStatus() const { return m_status; }
private:
@@ -104,6 +210,9 @@ class VideoSource {
friend class VideoSink;
public:
/**
* Video source kind.
*/
enum Kind {
/// Unknown video source.
kUnknown = CS_SOURCE_UNKNOWN,
@@ -359,6 +468,8 @@ class VideoSource {
explicit VideoSource(CS_Source handle) : m_handle(handle) {}
mutable CS_Status m_status = 0;
/// Video source handle.
CS_Source m_handle{0};
};
@@ -367,11 +478,19 @@ class VideoSource {
*/
class VideoCamera : public VideoSource {
public:
/**
* White balance.
*/
enum WhiteBalance {
/// Fixed indoor white balance.
kFixedIndoor = 3000,
/// Fixed outdoor white balance 1.
kFixedOutdoor1 = 4000,
/// Fixed outdoor white balance 2.
kFixedOutdoor2 = 5000,
/// Fixed fluorescent white balance 1.
kFixedFluorescent1 = 5100,
/// Fixed fluorescent white balance 2.
kFixedFlourescent2 = 5200
};
@@ -479,6 +598,9 @@ class UsbCamera : public VideoCamera {
*/
class HttpCamera : public VideoCamera {
public:
/**
* HTTP camera kind.
*/
enum HttpCameraKind {
/// Unknown camera kind.
kUnknown = CS_HTTP_UNKNOWN,
@@ -743,8 +865,18 @@ class VideoSink {
VideoSink& operator=(VideoSink other) noexcept;
~VideoSink();
/**
* Returns true if the VideoSink is valid.
*
* @return True if the VideoSink is valid.
*/
explicit operator bool() const { return m_handle != 0; }
/**
* Returns the VideoSink handle.
*
* @return The VideoSink handle.
*/
int GetHandle() const { return m_handle; }
bool operator==(const VideoSink& other) const {
@@ -988,17 +1120,23 @@ class ImageSink : public VideoSink {
class VideoEvent : public RawEvent {
public:
/**
* Get the source associated with the event (if any).
* Returns the source associated with the event (if any).
*
* @return The source associated with the event (if any).
*/
VideoSource GetSource() const;
/**
* Get the sink associated with the event (if any).
* Returns the sink associated with the event (if any).
*
* @return The sink associated with the event (if any).
*/
VideoSink GetSink() const;
/**
* Get the property associated with the event (if any).
* Returns the property associated with the event (if any).
*
* @return The property associated with the event (if any).
*/
VideoProperty GetProperty() const;
};

View File

@@ -29,51 +29,8 @@ model {
def applicationPath = binary.executable.file
def icon = file("$project.projectDir/src/main/native/mac/datalogtool.icns")
// Create the macOS bundle.
def bundleTask = project.tasks.create("bundleDataLogToolOsxApp" + binary.targetPlatform.architecture.name, Copy) {
description("Creates a macOS application bundle for DataLogTool")
from(file("$project.projectDir/Info.plist"))
into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/DataLogTool.app/Contents"))
into("MacOS") {
with copySpec {
from binary.executable.file
}
}
into("Resources") {
with copySpec {
from icon
}
}
inputs.property "HasDeveloperId", project.hasProperty("developerID")
doLast {
if (project.hasProperty("developerID")) {
// Get path to binary.
exec {
workingDir rootDir
def args = [
"sh",
"-c",
"codesign --force --strict --deep " +
"--timestamp --options=runtime " +
"--verbose -s ${project.findProperty("developerID")} " +
"$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/DataLogTool.app/"
]
commandLine args
}
}
}
}
// Reset the application path if we are creating a bundle.
if (binary.targetPlatform.operatingSystem.isMacOsX()) {
applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name")
project.build.dependsOn bundleTask
}
// Create the ZIP.
def task = project.tasks.create("copyDataLogToolExecutable" + binary.targetPlatform.architecture.name, Zip) {
def task = project.tasks.create("copyDataLogToolExecutable" + binary.targetPlatform.operatingSystem.name + binary.targetPlatform.architecture.name, Zip) {
description("Copies the DataLogTool executable to the outputs directory.")
destinationDirectory = outputsFolder
@@ -85,13 +42,63 @@ model {
into '/'
}
from(applicationPath)
if (binary.targetPlatform.operatingSystem.isWindows()) {
def exePath = binary.executable.file.absolutePath
exePath = exePath.substring(0, exePath.length() - 4)
def pdbPath = new File(exePath + '.pdb')
from(pdbPath)
}
into(nativeUtils.getPlatformPath(binary))
}
if (binary.targetPlatform.operatingSystem.isMacOsX()) {
// Create the macOS bundle.
def bundleTask = project.tasks.create("bundleDataLogToolOsxApp" + binary.targetPlatform.architecture.name, Copy) {
description("Creates a macOS application bundle for DataLogTool")
from(file("$project.projectDir/Info.plist"))
into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/DataLogTool.app/Contents"))
into("MacOS") {
with copySpec {
from binary.executable.file
}
}
into("Resources") {
with copySpec {
from icon
}
}
inputs.property "HasDeveloperId", project.hasProperty("developerID")
doLast {
if (project.hasProperty("developerID")) {
// Get path to binary.
exec {
workingDir rootDir
def args = [
"sh",
"-c",
"codesign --force --strict --deep " +
"--timestamp --options=runtime " +
"--verbose -s ${project.findProperty("developerID")} " +
"$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/DataLogTool.app/"
]
commandLine args
}
}
}
}
// Reset the application path if we are creating a bundle.
applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name")
task.from(applicationPath)
project.build.dependsOn bundleTask
bundleTask.dependsOn binary.tasks.link
task.dependsOn(bundleTask)
} else {
task.from(applicationPath)
}
task.dependsOn binary.tasks.link

View File

@@ -1,25 +1,25 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <string_view>
void Application(std::string_view saveDir);
#ifdef _WIN32
int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* pCmdLine,
int nCmdShow) {
int argc = __argc;
char** argv = __argv;
#else
int main(int argc, char** argv) {
#endif
std::string_view saveDir;
if (argc == 2) {
saveDir = argv[1];
}
Application(saveDir);
return 0;
}
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <string_view>
void Application(std::string_view saveDir);
#ifdef _WIN32
int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* pCmdLine,
int nCmdShow) {
int argc = __argc;
char** argv = __argv;
#else
int main(int argc, char** argv) {
#endif
std::string_view saveDir;
if (argc == 2) {
saveDir = argv[1];
}
Application(saveDir);
return 0;
}

View File

@@ -213,7 +213,13 @@ task generateJavaDocs(type: Javadoc) {
options.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
options.links("https://docs.opencv.org/4.x/javadoc/")
options.addStringOption("tag", "pre:a:Pre-Condition")
options.addBooleanOption("Xdoclint/package:-edu.wpi.first.math.proto," +
options.addBooleanOption("Xdoclint/package:" +
// TODO: v Document these, then remove them from the list
"-edu.wpi.first.hal," +
"-edu.wpi.first.hal.can," +
"-edu.wpi.first.hal.simulation," +
// TODO: ^ Document these, then remove them from the list
"-edu.wpi.first.math.proto," +
"-edu.wpi.first.math.controller.proto," +
"-edu.wpi.first.math.controller.struct," +
"-edu.wpi.first.math.geometry.proto," +

View File

@@ -40,12 +40,10 @@ public class FieldConfig {
public FieldConfig() {}
@SuppressWarnings("deprecation")
public URL getImageUrl() {
return getClass().getResource(Fields.kBaseResourceDir + m_fieldImage);
}
@SuppressWarnings("deprecation")
public InputStream getImageAsStream() {
return getClass().getResourceAsStream(Fields.kBaseResourceDir + m_fieldImage);
}

View File

@@ -3,17 +3,17 @@
"field-image": "2024-field.png",
"field-corners": {
"top-left": [
46,
36
150,
79
],
"bottom-right": [
1088,
544
2961,
1476
]
},
"field-size": [
54.27083,
26.2916
26.9375
],
"field-unit": "foot"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

@@ -97,8 +97,6 @@ model {
into '/'
}
from(applicationPath)
if (binary.targetPlatform.operatingSystem.isWindows()) {
def exePath = binary.executable.file.absolutePath
exePath = exePath.substring(0, exePath.length() - 4)
@@ -149,10 +147,13 @@ model {
// Reset the application path if we are creating a bundle.
applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name")
task.from(applicationPath)
project.build.dependsOn bundleTask
bundleTask.dependsOn binary.tasks.link
task.dependsOn(bundleTask)
} else {
task.from(applicationPath)
}
task.dependsOn binary.tasks.link

View File

@@ -96,7 +96,7 @@ static void NtInitialize() {
auto inst = nt::GetDefaultInstance();
auto poller = nt::CreateListenerPoller(inst);
nt::AddPolledListener(poller, inst, NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE);
nt::AddPolledLogger(poller, 0, 100);
nt::AddPolledLogger(poller, NT_LOG_INFO, 100);
gui::AddEarlyExecute([inst, poller] {
auto win = gui::GetSystemWindow();
if (!win) {

View File

@@ -5,6 +5,7 @@
#include "glass/other/FMS.h"
#include <imgui.h>
#include <imgui_stdlib.h>
#include <wpi/SmallString.h>
#include "glass/DataSource.h"
@@ -14,7 +15,7 @@ using namespace glass;
static const char* stations[] = {"Invalid", "Red 1", "Red 2", "Red 3",
"Blue 1", "Blue 2", "Blue 3"};
void glass::DisplayFMS(FMSModel* model) {
void glass::DisplayFMS(FMSModel* model, bool editableDsAttached) {
if (!model->Exists() || model->IsReadOnly()) {
return DisplayFMSReadOnly(model);
}
@@ -31,10 +32,17 @@ void glass::DisplayFMS(FMSModel* model) {
// DS Attached
if (auto data = model->GetDsAttachedData()) {
bool val = data->GetValue();
if (ImGui::Checkbox("DS Attached", &val)) {
model->SetDsAttached(val);
if (editableDsAttached) {
if (ImGui::Checkbox("DS Attached", &val)) {
model->SetDsAttached(val);
}
data->EmitDrag();
} else {
ImGui::Selectable("DS Attached: ");
data->EmitDrag();
ImGui::SameLine();
ImGui::TextUnformatted(val ? "Yes" : "No");
}
data->EmitDrag();
}
// Alliance Station
@@ -51,8 +59,7 @@ void glass::DisplayFMS(FMSModel* model) {
if (auto data = model->GetMatchTimeData()) {
double val = data->GetValue();
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
if (ImGui::InputDouble("Match Time", &val, 0, 0, "%.1f",
ImGuiInputTextFlags_EnterReturnsTrue)) {
if (ImGui::InputDouble("Match Time", &val, 0, 0, "%.1f")) {
model->SetMatchTime(val);
}
data->EmitDrag();
@@ -71,16 +78,12 @@ void glass::DisplayFMS(FMSModel* model) {
}
// Game Specific Message
// make buffer full 64 width, null terminated, for editability
wpi::SmallString<64> gameSpecificMessage;
model->GetGameSpecificMessage(gameSpecificMessage);
gameSpecificMessage.resize(63);
gameSpecificMessage.push_back('\0');
wpi::SmallString<64> gameSpecificMessageBuf;
std::string gameSpecificMessage{
model->GetGameSpecificMessage(gameSpecificMessageBuf)};
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
if (ImGui::InputText("Game Specific", gameSpecificMessage.data(),
gameSpecificMessage.size(),
ImGuiInputTextFlags_EnterReturnsTrue)) {
model->SetGameSpecificMessage(gameSpecificMessage.data());
if (ImGui::InputText("Game Specific", &gameSpecificMessage)) {
model->SetGameSpecificMessage(gameSpecificMessage);
}
}
@@ -144,9 +147,10 @@ void glass::DisplayFMSReadOnly(FMSModel* model) {
}
}
wpi::SmallString<64> gameSpecificMessage;
model->GetGameSpecificMessage(gameSpecificMessage);
ImGui::Text("Game Specific: %s", exists ? gameSpecificMessage.c_str() : "?");
wpi::SmallString<64> gameSpecificMessageBuf;
std::string_view gameSpecificMessage =
model->GetGameSpecificMessage(gameSpecificMessageBuf);
ImGui::Text("Game Specific: %s", exists ? gameSpecificMessage.data() : "?");
if (!exists) {
ImGui::PopStyleColor();

View File

@@ -222,8 +222,8 @@ class ObjectInfo {
class FieldInfo {
public:
static constexpr auto kDefaultWidth = 15.98_m;
static constexpr auto kDefaultHeight = 8.21_m;
static constexpr auto kDefaultWidth = 16.541052_m;
static constexpr auto kDefaultHeight = 8.211_m;
explicit FieldInfo(Storage& storage);
@@ -343,7 +343,7 @@ static bool InputPose(frc::Pose2d* pose) {
}
FieldInfo::FieldInfo(Storage& storage)
: m_builtin{storage.GetString("builtin")},
: m_builtin{storage.GetString("builtin", "2024 Crescendo")},
m_filename{storage.GetString("image")},
m_width{storage.GetFloat("width", kDefaultWidth.to<float>())},
m_height{storage.GetFloat("height", kDefaultHeight.to<float>())},
@@ -508,6 +508,16 @@ bool FieldInfo::LoadJson(std::span<const char> is, std::string_view filename) {
height = units::convert<units::feet, units::meters>(height);
}
// check scaling
int fieldWidth = m_right - m_left;
int fieldHeight = m_bottom - m_top;
if (std::abs((fieldWidth / width) - (fieldHeight / height)) > 0.3) {
fmt::print(stderr,
"GUI: Field X and Y scaling substantially different: "
"xscale={} yscale={}\n",
(fieldWidth / width), (fieldHeight / height));
}
if (!filename.empty()) {
// the image filename is relative to the json file
auto pathname = fs::path{filename}.replace_filename(image).string();
@@ -560,23 +570,29 @@ FieldFrameData FieldInfo::GetFrameData(ImVec2 min, ImVec2 max) const {
// fit the image into the window
if (m_texture && m_imageHeight != 0 && m_imageWidth != 0) {
gui::MaxFit(&min, &max, m_imageWidth, m_imageHeight);
} else {
gui::MaxFit(&min, &max, m_width, m_height);
}
FieldFrameData ffd;
ffd.imageMin = min;
ffd.imageMax = max;
// size down the box by the image corners (if any)
if (m_bottom > 0 && m_right > 0) {
min.x += m_left * (max.x - min.x) / m_imageWidth;
min.y += m_top * (max.y - min.y) / m_imageHeight;
max.x -= (m_imageWidth - m_right) * (max.x - min.x) / m_imageWidth;
max.y -= (m_imageHeight - m_bottom) * (max.y - min.y) / m_imageHeight;
if (m_bottom > 0 && m_right > 0 && m_imageWidth != 0) {
// size down the box by the image corners
float scale = (max.x - min.x) / m_imageWidth;
min.x += m_left * scale;
min.y += m_top * scale;
max.x -= (m_imageWidth - m_right) * scale;
max.y -= (m_imageHeight - m_bottom) * scale;
} else if ((max.x - min.x) > 40 && (max.y - min.y > 40)) {
// ensure there's some padding
min.x += 20;
max.x -= 20;
min.y += 20;
max.y -= 20;
}
// draw the field "active area" as a yellow boundary box
gui::MaxFit(&min, &max, m_width, m_height);
ffd.min = min;
ffd.max = max;
ffd.scale = (max.x - min.x) / m_width;

View File

@@ -46,8 +46,9 @@ class FMSModel : public Model {
*
* @param matchTimeEnabled If not null, a checkbox is displayed for
* "enable match time" linked to this value
* @param editableDsAttached If true, DS attached should be editable
*/
void DisplayFMS(FMSModel* model);
void DisplayFMS(FMSModel* model, bool editableDsAttached);
void DisplayFMSReadOnly(FMSModel* model);
} // namespace glass

View File

@@ -63,7 +63,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
[](Window* win, Model* model, const char*) {
win->SetFlags(ImGuiWindowFlags_AlwaysAutoResize);
return MakeFunctionView(
[=] { DisplayFMS(static_cast<FMSModel*>(model)); });
[=] { DisplayFMS(static_cast<FMSModel*>(model), true); });
});
provider.Register(
NTDigitalInputModel::kType,

View File

@@ -20,7 +20,6 @@ generatedFileExclude {
modifiableFileExclude {
\.patch$
\.png$
\.py$
\.so$
}

View File

@@ -10,40 +10,56 @@ def main():
# Gets the folder this script is in (the hal/ directory)
HAL_ROOT = pathlib.Path(__file__).parent
java_package = "edu/wpi/first/hal"
(HAL_ROOT/"src/generated/main/native/include/hal").mkdir(parents=True, exist_ok=True)
(HAL_ROOT/f"src/generated/main/java/{java_package}").mkdir(parents=True, exist_ok=True)
usage_reporting_types_cpp = []
# fmt: off
(HAL_ROOT / "src/generated/main/native/include/hal").mkdir(parents=True, exist_ok=True)
(HAL_ROOT / f"src/generated/main/java/{java_package}").mkdir(parents=True, exist_ok=True)
# fmt: on
usage_reporting_types_cpp = []
usage_reporting_instances_cpp = []
usage_reporting_types = []
usage_reporting_instances = []
with open(HAL_ROOT/"src/generate/Instances.txt") as instances:
with open(HAL_ROOT / "src/generate/Instances.txt") as instances:
for instance in instances:
usage_reporting_instances_cpp.append(f" {instance.strip()},")
usage_reporting_instances.append(
f" /** {instance.strip()}. */\n"
f" public static final int {instance.strip()};")
f" public static final int {instance.strip()};"
)
with open(HAL_ROOT/"src/generate/ResourceType.txt") as resource_types:
with open(HAL_ROOT / "src/generate/ResourceType.txt") as resource_types:
for resource_type in resource_types:
usage_reporting_types_cpp.append(f" {resource_type.strip()},")
usage_reporting_types.append(
f" /** {resource_type.strip()}. */\n"
f" public static final int {resource_type.strip()};")
f" public static final int {resource_type.strip()};"
)
with open(HAL_ROOT/"src/generate/FRCNetComm.java.in") as java_usage_reporting:
contents = (java_usage_reporting.read()
with open(HAL_ROOT / "src/generate/FRCNetComm.java.in") as java_usage_reporting:
contents = (
# fmt: off
java_usage_reporting.read()
.replace(r"${usage_reporting_types}", "\n".join(usage_reporting_types))
.replace(r"${usage_reporting_instances}", "\n".join(usage_reporting_instances)))
with open(HAL_ROOT/f"src/generated/main/java/{java_package}/FRCNetComm.java", "w") as java_out:
.replace(r"${usage_reporting_instances}", "\n".join(usage_reporting_instances))
# fmt: on
)
with open(
HAL_ROOT / f"src/generated/main/java/{java_package}/FRCNetComm.java", "w"
) as java_out:
java_out.write(contents)
with open(HAL_ROOT/"src/generate/FRCUsageReporting.h.in") as cpp_usage_reporting:
contents = (cpp_usage_reporting.read()
with open(HAL_ROOT / "src/generate/FRCUsageReporting.h.in") as cpp_usage_reporting:
contents = (
# fmt: off
cpp_usage_reporting.read()
.replace(r"${usage_reporting_types_cpp}", "\n".join(usage_reporting_types_cpp))
.replace(r"${usage_reporting_instances_cpp}", "\n".join(usage_reporting_instances_cpp)))
.replace(r"${usage_reporting_instances_cpp}", "\n".join(usage_reporting_instances_cpp))
# fmt: on
)
with open(HAL_ROOT/"src/generated/main/native/include/hal/FRCUsageReporting.h", "w") as cpp_out:
with open(
HAL_ROOT / "src/generated/main/native/include/hal/FRCUsageReporting.h", "w"
) as cpp_out:
cpp_out.write(contents)

View File

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

View File

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

View File

@@ -7,6 +7,9 @@ package edu.wpi.first.hal.can;
import edu.wpi.first.hal.communication.NIRioStatus;
import edu.wpi.first.hal.util.UncleanStatusException;
/**
* Checks the status of a CAN message and throws an exception of the appropriate type if necessary.
*/
public final class CANExceptionFactory {
// FRC Error codes
static final int ERR_CANSessionMux_InvalidBuffer = -44086;

View File

@@ -16,11 +16,16 @@ import java.nio.IntBuffer;
*/
@SuppressWarnings("MethodName")
public class CANJNI extends JNIWrapper {
/** Flag for sending a CAN message once. */
public static final int CAN_SEND_PERIOD_NO_REPEAT = 0;
/** Flag for stopping periodic CAN message sends. */
public static final int CAN_SEND_PERIOD_STOP_REPEATING = -1;
/* Flags in the upper bits of the messageID */
/** Mask for "is frame remote" in message ID. */
public static final int CAN_IS_FRAME_REMOTE = 0x80000000;
/** Mask for "is frame 11 bits" in message ID. */
public static final int CAN_IS_FRAME_11BIT = 0x40000000;
/** Default constructor. */

View File

@@ -4,13 +4,24 @@
package edu.wpi.first.hal.communication;
/** NI RIO status. */
public class NIRioStatus {
/** RIO status offset. */
public static final int kRioStatusOffset = -63000;
/** Success. */
public static final int kRioStatusSuccess = 0;
/** Buffer invalid size. */
public static final int kRIOStatusBufferInvalidSize = kRioStatusOffset - 80;
/** Operation timed out. */
public static final int kRIOStatusOperationTimedOut = -52007;
/** Feature not supported. */
public static final int kRIOStatusFeatureNotSupported = kRioStatusOffset - 193;
/** Resource not initialized. */
public static final int kRIOStatusResourceNotInitialized = -52010;
/** Default constructor. */

View File

@@ -198,6 +198,10 @@ void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle,
return;
}
if (length == 0) {
return;
}
std::memcpy(led->ledBuffer, data, length * sizeof(HAL_AddressableLEDData));
asm("dmb");

View File

@@ -10,6 +10,8 @@
#include <memory>
#include <type_traits>
#include <fmt/format.h>
#include "AnalogInternal.h"
#include "ConstantsInternal.h"
#include "DigitalInternal.h"
@@ -676,7 +678,7 @@ void HAL_StartDMA(HAL_DMAHandle handle, int32_t queueDepth, int32_t* status) {
SET_SIZE(Enable_DutyCycle_Low);
SET_SIZE(Enable_DutyCycle_High);
#undef SET_SIZE
dma->captureStore.captureSize = accum_size + 1;
dma->captureStore.captureSize = accum_size + 2;
}
uint32_t byteDepth = queueDepth * dma->captureStore.captureSize;
@@ -734,12 +736,22 @@ enum HAL_DMAReadStatus HAL_ReadDMADirect(void* dmaPointer,
static_cast<uint32_t>(timeoutSeconds * 1000),
&remainingBytes, status);
if ((remainingBytes % dma->captureStore.captureSize) != 0) {
fmt::print(
"Remaining bytes {} is not a multiple of capture size {}. This is "
"likely a "
"bug in WPILib. Please report this issue with a copy of your code.\n",
remainingBytes, dma->captureStore.captureSize);
}
*remainingOut = remainingBytes / dma->captureStore.captureSize;
if (*status == 0) {
uint32_t lower_sample =
uint64_t upper_sample =
dmaSample->readBuffer[dma->captureStore.captureSize - 1];
dmaSample->timeStamp = HAL_ExpandFPGATime(lower_sample, status);
uint64_t lower_sample =
dmaSample->readBuffer[dma->captureStore.captureSize - 2];
dmaSample->timeStamp = (upper_sample << 32) + lower_sample;
if (*status != 0) {
return HAL_DMA_ERROR;
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -14,11 +14,24 @@
// These are copies of defines located in CANSessionMux.h prepended with HAL_
/**
* Flag for sending a CAN message once.
*/
#define HAL_CAN_SEND_PERIOD_NO_REPEAT 0
/**
* Flag for stopping periodic CAN message sends.
*/
#define HAL_CAN_SEND_PERIOD_STOP_REPEATING -1
/* Flags in the upper bits of the messageID */
/**
* Mask for "is frame remote" in message ID.
*/
#define HAL_CAN_IS_FRAME_REMOTE 0x80000000
/**
* Mask for "is frame 11 bits" in message ID.
*/
#define HAL_CAN_IS_FRAME_11BIT 0x40000000
#define HAL_ERR_CANSessionMux_InvalidBuffer -44086

View File

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

View File

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

View File

@@ -28,7 +28,7 @@ void DriverStationData::ResetData() {
test.Reset(false);
eStop.Reset(false);
fmsAttached.Reset(false);
dsAttached.Reset(true);
dsAttached.Reset(false);
allianceStationId.Reset(static_cast<HAL_AllianceStationID>(0));
matchTime.Reset(-1.0);

View File

@@ -122,7 +122,7 @@ class DriverStationData {
SimDataValue<HAL_Bool, HAL_MakeBoolean, GetEStopName> eStop{false};
SimDataValue<HAL_Bool, HAL_MakeBoolean, GetFmsAttachedName> fmsAttached{
false};
SimDataValue<HAL_Bool, HAL_MakeBoolean, GetDsAttachedName> dsAttached{true};
SimDataValue<HAL_Bool, HAL_MakeBoolean, GetDsAttachedName> dsAttached{false};
SimDataValue<HAL_AllianceStationID, MakeAllianceStationIdValue,
GetAllianceStationIdName>
allianceStationId{static_cast<HAL_AllianceStationID>(0)};

View File

@@ -1,6 +1,6 @@
= Network Tables Alloy Model
Alloy (http://alloy.mit.edu/alloy/) is a formal logic tool that can analyze
[Alloy](https://www.csail.mit.edu/research/alloy) is a formal logic tool that can analyze
first-order logic expressions. Under the proposed sequence number -based
protocol, assuming that all nodes start from the same state, Alloy is unable to
find a way where two nodes with the same sequence number have different state

View File

@@ -236,6 +236,12 @@ public interface GenericPublisher extends Publisher, Consumer<NetworkTableValue>
boolean setDefault{{ t.TypeName }}({{ t.java.ValueType }} defaultValue);
{% endif -%}
{% if t.java.WrapValueType %}
/**
* Sets the entry's value if it does not exist.
*
* @param defaultValue the default value to set
* @return False if the entry exists with a different type
*/
boolean setDefault{{ t.TypeName }}({{ t.java.WrapValueType }} defaultValue);
{% endif -%}
{% endfor %}

View File

@@ -608,6 +608,6 @@ public final class NetworkTableEntry implements Publisher, Subscriber {
}
private final Topic m_topic;
protected int m_handle;
private final int m_handle;
}

View File

@@ -68,6 +68,11 @@ public final class NetworkTableInstance implements AutoCloseable {
this.value = value;
}
/**
* Returns the network mode value.
*
* @return The network mode value.
*/
public int getValue() {
return value;
}

View File

@@ -14,6 +14,7 @@ import java.util.EnumSet;
import java.util.OptionalLong;
import java.util.concurrent.atomic.AtomicBoolean;
/** NetworkTables JNI. */
public final class NetworkTablesJNI {
static boolean libraryLoaded = false;
static RuntimeLoader<NetworkTablesJNI> loader = null;
@@ -82,139 +83,494 @@ public final class NetworkTablesJNI {
return new PubSubOptions(options);
}
/**
* Returns default instance handle.
*
* @return Default instance handle.
*/
public static native int getDefaultInstance();
/**
* Creates an NT instance.
*
* @return NT instance handle.
*/
public static native int createInstance();
/**
* Destroys an NT instance.
*
* @param inst NT instance handle.
*/
public static native void destroyInstance(int inst);
/**
* Returns NT instance from handle.
*
* @param handle NT instance handle.
* @return NT instance.
*/
public static native int getInstanceFromHandle(int handle);
private static native int getEntryImpl(
int topic, int type, String typeStr, PubSubOptions options);
/**
* Returns NT entry handle.
*
* @param inst NT instance handle.
* @param key NT entry key.
* @return NT entry handle.
*/
public static native int getEntry(int inst, String key);
/**
* Returns NT entry handle.
*
* @param topic NT entry topic.
* @param type NT entry type.
* @param typeStr NT entry type as a string.
* @param options NT entry pubsub options.
* @return NT entry handle.
*/
public static int getEntry(
int topic, int type, String typeStr, PubSubOptions options) {
return getEntryImpl(topic, type, typeStr, options);
}
/**
* Returns NT entry handle.
*
* @param topic NT entry topic.
* @param type NT entry type.
* @param typeStr NT entry type as a string.
* @param options NT entry pubsub options.
* @return NT entry handle.
*/
public static int getEntry(
int topic, int type, String typeStr, PubSubOption... options) {
return getEntryImpl(topic, type, typeStr, buildOptions(options));
}
/**
* Returns NT entry name.
*
* @param entry NT entry handle.
* @return NT entry name.
*/
public static native String getEntryName(int entry);
/**
* Returns NT entry last change time in microseconds.
*
* @param entry NT entry handle.
* @return NT entry last change time in microseconds.
*/
public static native long getEntryLastChange(int entry);
/**
* Returns NT entry type.
*
* @param entry NT entry handle.
* @return NT entry type.
*/
public static native int getType(int entry);
/* Topic functions */
/**
* Returns list of topic handles.
*
* @param inst NT instance handle.
* @param prefix Topic prefix.
* @param types Topic types.
* @return List of topic handles.
*/
public static native int[] getTopics(int inst, String prefix, int types);
/**
* Returns list of topic handles.
*
* @param inst NT instance handle.
* @param prefix Topic prefix.
* @param types Topic types as strings.
* @return List of topic handles.
*/
public static native int[] getTopicsStr(int inst, String prefix, String[] types);
/**
* Returns list of topic infos.
*
* @param instObject NT instance.
* @param inst NT instance handle.
* @param prefix Topic prefix.
* @param types Topic types.
* @return List of topic infos.
*/
public static native TopicInfo[] getTopicInfos(
NetworkTableInstance instObject, int inst, String prefix, int types);
/**
* Returns list of topic infos.
*
* @param instObject NT instance.
* @param inst NT instance handle.
* @param prefix Topic prefix.
* @param types Topic types as strings.
* @return List of topic infos.
*/
public static native TopicInfo[] getTopicInfosStr(
NetworkTableInstance instObject, int inst, String prefix, String[] types);
/**
* Returns Topic handle.
*
* @param inst NT instance handle.
* @param name Topic name.
* @return Topic handle.
*/
public static native int getTopic(int inst, String name);
/**
* Returns topic name.
*
* @param topic Topic handle.
* @return Topic name.
*/
public static native String getTopicName(int topic);
/**
* Returns topic type.
*
* @param topic Topic handle.
* @return Topic type.
*/
public static native int getTopicType(int topic);
/**
* Sets topic persistency.
*
* @param topic Topic handle.
* @param value True if topic should be persistent.
*/
public static native void setTopicPersistent(int topic, boolean value);
/**
* Returns true if topic is persistent.
*
* @param topic Topic handle.
* @return True if topic is persistent.
*/
public static native boolean getTopicPersistent(int topic);
/**
* Sets whether topic is retained.
*
* @param topic Topic handle.
* @param value True if topic should be retained.
*/
public static native void setTopicRetained(int topic, boolean value);
/**
* Returns true if topic is retained.
*
* @param topic Topic handle.
* @return True if topic is retained.
*/
public static native boolean getTopicRetained(int topic);
/**
* Sets topic caching.
*
* @param topic Topic handle.
* @param value True if topic should be cached.
*/
public static native void setTopicCached(int topic, boolean value);
/**
* Returns true if topic is cached.
*
* @param topic Topic handle.
* @return True if topic is cached.
*/
public static native boolean getTopicCached(int topic);
/**
* Returns topic type as string.
*
* @param topic Topic handle.
* @return Topic type as string.
*/
public static native String getTopicTypeString(int topic);
/**
* Returns true if topic exists.
*
* @param topic Topic handle.
* @return True if topic exists.
*/
public static native boolean getTopicExists(int topic);
/**
* Returns topic property.
*
* @param topic Topic handle.
* @param name Property name.
* @return Topic property.
*/
public static native String getTopicProperty(int topic, String name);
/**
* Sets topic property.
*
* @param topic Topic handle.
* @param name Property name.
* @param value Property value.
*/
public static native void setTopicProperty(int topic, String name, String value);
/**
* Deletes topic property.
*
* @param topic Topic handle.
* @param name Property name.
*/
public static native void deleteTopicProperty(int topic, String name);
/**
* Returns topic properties.
*
* @param topic Topic handle.
* @return Topic properties.
*/
public static native String getTopicProperties(int topic);
/**
* Sets topic properties.
*
* @param topic Topic handle.
* @param properties Topic properties.
*/
public static native void setTopicProperties(int topic, String properties);
/**
* Subscribes to topic.
*
* @param topic Topic handle.
* @param type Topic type.
* @param typeStr Topic type as a string.
* @param options Pubsub options.
* @return Subscriber handle.
*/
public static native int subscribe(
int topic, int type, String typeStr, PubSubOptions options);
/**
* Subscribes to topic.
*
* @param topic Topic handle.
* @param type Topic type.
* @param typeStr Topic type as a string.
* @param options Pubsub options.
* @return Subscriber handle.
*/
public static int subscribe(
int topic, int type, String typeStr, PubSubOption... options) {
return subscribe(topic, type, typeStr, buildOptions(options));
}
/**
* Unsubscribes from topic.
*
* @param sub Subscriber handle.
*/
public static native void unsubscribe(int sub);
/**
* Publishes topic.
*
* @param topic Topic handle.
* @param type Topic type.
* @param typeStr Topic type as a string.
* @param options Pubsub options.
* @return Publish handle.
*/
public static native int publish(
int topic, int type, String typeStr, PubSubOptions options);
/**
* Publishes topic.
*
* @param topic Topic handle.
* @param type Topic type.
* @param typeStr Topic type as a string.
* @param options Pubsub options.
* @return Publish handle.
*/
public static int publish(
int topic, int type, String typeStr, PubSubOption... options) {
return publish(topic, type, typeStr, buildOptions(options));
}
/**
* Publishes topic.
*
* @param topic Topic handle.
* @param type Topic type.
* @param typeStr Topic type as a string.
* @param properties Topic properties.
* @param options Pubsub options.
* @return Publish handle.
*/
public static native int publishEx(
int topic, int type, String typeStr, String properties, PubSubOptions options);
/**
* Publishes topic.
*
* @param topic Topic handle.
* @param type Topic type.
* @param typeStr Topic type as a string.
* @param properties Topic properties.
* @param options Pubsub options.
* @return Publish handle.
*/
public static int publishEx(
int topic, int type, String typeStr, String properties, PubSubOption... options) {
return publishEx(topic, type, typeStr, properties, buildOptions(options));
}
/**
* Unpublishes topic.
*
* @param pubentry Publish entry handle.
*/
public static native void unpublish(int pubentry);
/**
* Releases NT entry.
*
* @param entry NT entry handle.
*/
public static native void releaseEntry(int entry);
/**
* Relesaes pubsub entry.
*
* @param pubsubentry Pubsub entry handle.
*/
public static native void release(int pubsubentry);
/**
* Returns topic from pubsub entry handle.
*
* @param pubsubentry Pubsub entry handle.
* @return Topic handle.
*/
public static native int getTopicFromHandle(int pubsubentry);
/**
* Subscribes to multiple topics.
*
* @param inst NT instance handle.
* @param prefixes List of topic prefixes.
* @param options Pubsub options.
* @return Subscribe handle.
*/
public static native int subscribeMultiple(int inst, String[] prefixes, PubSubOptions options);
/**
* Subscribes to multiple topics.
*
* @param inst NT instance handle.
* @param prefixes List of topic prefixes.
* @param options Pubsub options.
* @return Subscribe handle.
*/
public static int subscribeMultiple(int inst, String[] prefixes, PubSubOption... options) {
return subscribeMultiple(inst, prefixes, buildOptions(options));
}
/**
* Unsubscribes from multiple topics.
*
* @param sub Subscribe handle.
*/
public static native void unsubscribeMultiple(int sub);
{% for t in types %}
/**
* Returns timestamped topic value as an atomic {{ t.TypeName }}.
*
* @param subentry Subentry handle.
* @param defaultValue Default value.
* @return Timestamped topic value.
*/
public static native Timestamped{{ t.TypeName }} getAtomic{{ t.TypeName }}(
int subentry, {{ t.java.ValueType }} defaultValue);
/**
* Returns queued timestamped topic values.
*
* @param subentry Subentry handle.
* @return List of timestamped topic values.
*/
public static native Timestamped{{ t.TypeName }}[] readQueue{{ t.TypeName }}(int subentry);
/**
* Returns queued topic values.
*
* @param subentry Subentry handle.
* @return List of topic values.
*/
public static native {{ t.java.ValueType }}[] readQueueValues{{ t.TypeName }}(int subentry);
{% if t.TypeName == "Raw" %}
/**
* Sets raw topic value.
*
* @param entry Entry handle.
* @param time Time in microseconds.
* @param value Raw value buffer.
* @return True if set succeeded.
*/
public static boolean setRaw(int entry, long time, byte[] value) {
return setRaw(entry, time, value, 0, value.length);
}
/**
* Sets raw topic value.
*
* @param entry Entry handle.
* @param time Time in microseconds.
* @param value Raw value buffer.
* @param start Value's offset into buffer.
* @param len Length of value in buffer.
* @return True if set succeeded.
*/
public static native boolean setRaw(int entry, long time, byte[] value, int start, int len);
/**
* Sets raw topic value.
*
* @param entry Entry handle.
* @param time Time in microseconds.
* @param value Raw value buffer.
* @return True if set succeeded.
*/
public static boolean setRaw(int entry, long time, ByteBuffer value) {
int pos = value.position();
return setRaw(entry, time, value, pos, value.capacity() - pos);
}
/**
* Sets raw topic value.
*
* @param entry Entry handle.
* @param time Time in microseconds.
* @param value Raw value buffer.
* @param start Value's offset into buffer.
* @param len Length of value in buffer.
* @return True if set succeeded.
*/
public static boolean setRaw(int entry, long time, ByteBuffer value, int start, int len) {
if (value.isDirect()) {
if (start < 0) {
@@ -234,23 +590,84 @@ public final class NetworkTablesJNI {
}
}
/**
* Sets raw topic value buffer.
*
* @param entry Entry handle.
* @param time Time in microseconds.
* @param value Raw value buffer.
* @param start Value's offset into buffer.
* @param len Length of value in buffer.
* @return True if set succeeded.
*/
private static native boolean setRawBuffer(int entry, long time, ByteBuffer value, int start, int len);
{% else %}
/**
* Sets topic value.
*
* @param entry Entry handle.
* @param time Time in microseconds.
* @param value Topic value.
* @return True if set succeeded.
*/
public static native boolean set{{ t.TypeName }}(int entry, long time, {{ t.java.ValueType }} value);
{% endif %}
/**
* Returns topic value.
*
* @param entry Entry handle.
* @param defaultValue Default value.
* @return Topic value.
*/
public static native {{ t.java.ValueType }} get{{ t.TypeName }}(int entry, {{ t.java.ValueType }} defaultValue);
{% if t.TypeName == "Raw" %}
/**
* Sets default raw topic value.
*
* @param entry Entry handle.
* @param time Time in microseconds.
* @param defaultValue Default value.
* @return True if set succeeded.
*/
public static boolean setDefaultRaw(int entry, long time, byte[] defaultValue) {
return setDefaultRaw(entry, time, defaultValue, 0, defaultValue.length);
}
/**
* Sets default raw topic value.
*
* @param entry Entry handle.
* @param time Time in microseconds.
* @param defaultValue Default value.
* @param start Value's offset into buffer.
* @param len Length of value in buffer.
* @return True if set succeeded.
*/
public static native boolean setDefaultRaw(int entry, long time, byte[] defaultValue, int start, int len);
/**
* Sets default raw topic value.
*
* @param entry Entry handle.
* @param time Time in microseconds.
* @param defaultValue Default value.
* @return True if set succeeded.
*/
public static boolean setDefaultRaw(int entry, long time, ByteBuffer defaultValue) {
int pos = defaultValue.position();
return setDefaultRaw(entry, time, defaultValue, pos, defaultValue.limit() - pos);
}
/**
* Sets default raw topic value.
*
* @param entry Entry handle.
* @param time Time in microseconds.
* @param defaultValue Default value.
* @param start Value's offset into buffer.
* @param len Length of value in buffer.
* @return True if set succeeded.
*/
public static boolean setDefaultRaw(int entry, long time, ByteBuffer defaultValue, int start, int len) {
if (defaultValue.isDirect()) {
if (start < 0) {
@@ -270,25 +687,91 @@ public final class NetworkTablesJNI {
}
}
/**
* Sets default raw topic value buffer.
*
* @param entry Entry handle.
* @param time Time in microseconds.
* @param defaultValue Default value.
* @param start Value's offset into buffer.
* @param len Length of value in buffer.
* @return True if set succeeded.
*/
private static native boolean setDefaultRawBuffer(int entry, long time, ByteBuffer defaultValue, int start, int len);
{% else %}
/**
* Sets default topic value.
*
* @param entry Entry handle.
* @param time Time in microseconds.
* @param defaultValue Default value.
* @return True if set succeeded.
*/
public static native boolean setDefault{{ t.TypeName }}(int entry, long time, {{ t.java.ValueType }} defaultValue);
{% endif %}
{% endfor %}
/**
* Returns queued subentry values.
*
* @param subentry Subentry handle.
* @return List of queued subentry values.
*/
public static native NetworkTableValue[] readQueueValue(int subentry);
/**
* Returns entry's NT value.
*
* @param entry Entry handle.
* @return Entry's NT value.
*/
public static native NetworkTableValue getValue(int entry);
/**
* Sets entry flags.
*
* @param entry Entry handle.
* @param flags Entry flags.
*/
public static native void setEntryFlags(int entry, int flags);
/**
* Returns entry flags.
*
* @param entry Entry handle.
* @return Entry flags.
*/
public static native int getEntryFlags(int entry);
/**
* Returns topic info.
*
* @param inst NT instance handle.
* @param topic Topic handle.
* @return Topic info.
*/
public static native TopicInfo getTopicInfo(NetworkTableInstance inst, int topic);
/**
* Creates a listener poller.
*
* @param inst NT instance handle.
* @return Listener poller handle.
*/
public static native int createListenerPoller(int inst);
/**
* Destroys listener poller.
*
* @param poller Listener poller handle.
*/
public static native void destroyListenerPoller(int poller);
/**
* Converts NT event kinds to mask.
*
* @param kinds Enum set of NT event kinds.
* @return NT event mask.
*/
private static int kindsToMask(EnumSet<NetworkTableEvent.Kind> kinds) {
int mask = 0;
for (NetworkTableEvent.Kind kind : kinds) {
@@ -297,80 +780,316 @@ public final class NetworkTablesJNI {
return mask;
}
/**
* Adds listener.
*
* @param poller Listener poller handle.
* @param prefixes Topic prefixes.
* @param kinds Enum set of NT event kinds.
* @return Listener handle.
*/
public static int addListener(int poller, String[] prefixes, EnumSet<NetworkTableEvent.Kind> kinds) {
return addListener(poller, prefixes, kindsToMask(kinds));
}
/**
* Adds listener.
*
* @param poller Listener poller handle.
* @param handle Topic handle.
* @param kinds Enum set of NT event kinds.
* @return Listener handle.
*/
public static int addListener(int poller, int handle, EnumSet<NetworkTableEvent.Kind> kinds) {
return addListener(poller, handle, kindsToMask(kinds));
}
/**
* Adds listener.
*
* @param poller Listener poller handle.
* @param prefixes Topic prefixes.
* @param mask NT event mask.
* @return Listener handle.
*/
public static native int addListener(int poller, String[] prefixes, int mask);
/**
* Adds listener.
*
* @param poller Listener poller handle.
* @param handle Topic handle.
* @param mask NT event mask.
* @return Listener handle.
*/
public static native int addListener(int poller, int handle, int mask);
/**
* Returns NT events from listener queue.
*
* @param inst NT instance handle.
* @param poller Listener poller handle.
* @return List of NT events.
*/
public static native NetworkTableEvent[] readListenerQueue(
NetworkTableInstance inst, int poller);
/**
* Removes listener.
*
* @param listener Listener handle.
*/
public static native void removeListener(int listener);
/**
* Returns network mode.
*
* @param inst NT instance handle.
* @return Network mode.
*/
public static native int getNetworkMode(int inst);
/**
* Starts local-only operation. Prevents calls to startServer or startClient from taking effect.
* Has no effect if startServer or startClient has already been called.
*
* @param inst NT instance handle.
*/
public static native void startLocal(int inst);
/**
* Stops local-only operation. startServer or startClient can be called after this call to start
* a server or client.
*
* @param inst NT instance handle.
*/
public static native void stopLocal(int inst);
/**
* Starts a server using the specified filename, listening address, and port.
*
* @param inst NT instance handle.
* @param persistFilename the name of the persist file to use
* @param listenAddress the address to listen on, or empty to listen on any address
* @param port3 port to communicate over (NT3)
* @param port4 port to communicate over (NT4)
*/
public static native void startServer(
int inst, String persistFilename, String listenAddress, int port3, int port4);
/**
* Stops the server if it is running.
*
* @param inst NT instance handle.
*/
public static native void stopServer(int inst);
/**
* Starts a NT3 client. Use SetServer or SetServerTeam to set the server name and port.
*
* @param inst NT instance handle.
* @param identity network identity to advertise (cannot be empty string)
*/
public static native void startClient3(int inst, String identity);
/**
* Starts a NT4 client. Use SetServer or SetServerTeam to set the server name and port.
*
* @param inst NT instance handle.
* @param identity network identity to advertise (cannot be empty string)
*/
public static native void startClient4(int inst, String identity);
/**
* Stops the client if it is running.
*
* @param inst NT instance handle.
*/
public static native void stopClient(int inst);
/**
* Sets server address and port for client (without restarting client).
*
* @param inst NT instance handle.
* @param serverName server name
* @param port port to communicate over
*/
public static native void setServer(int inst, String serverName, int port);
/**
* Sets server addresses and ports for client (without restarting client). The client will
* attempt to connect to each server in round robin fashion.
*
* @param inst NT instance handle.
* @param serverNames array of server names
* @param ports array of port numbers (0=default)
*/
public static native void setServer(int inst, String[] serverNames, int[] ports);
/**
* Sets server addresses and port for client (without restarting client). Connects using commonly
* known robot addresses for the specified team.
*
* @param inst NT instance handle.
* @param team team number
* @param port port to communicate over
*/
public static native void setServerTeam(int inst, int team, int port);
/**
* Disconnects the client if it's running and connected. This will automatically start
* reconnection attempts to the current server list.
*
* @param inst NT instance handle.
*/
public static native void disconnect(int inst);
/**
* Starts requesting server address from Driver Station. This connects to the Driver Station
* running on localhost to obtain the server IP address.
*
* @param inst NT instance handle.
* @param port server port to use in combination with IP from DS
*/
public static native void startDSClient(int inst, int port);
/**
* Stops requesting server address from Driver Station.
*
* @param inst NT instance handle.
*/
public static native void stopDSClient(int inst);
/**
* Flushes all updated values immediately to the local client/server. This does not flush to the
* network.
*
* @param inst NT instance handle.
*/
public static native void flushLocal(int inst);
/**
* Flushes all updated values immediately to the network. Note: This is rate-limited to protect
* the network from flooding. This is primarily useful for synchronizing network updates with
* user code.
*
* @param inst NT instance handle.
*/
public static native void flush(int inst);
/**
* Gets information on the currently established network connections. If operating as a client,
* this will return either zero or one values.
*
* @param inst NT instance handle.
* @return array of connection information
*/
public static native ConnectionInfo[] getConnections(int inst);
/**
* Return whether or not the instance is connected to another node.
*
* @param inst NT instance handle.
* @return True if connected.
*/
public static native boolean isConnected(int inst);
/**
* Get the time offset between server time and local time. Add this value to local time to get
* the estimated equivalent server time. In server mode, this always returns 0. In client mode,
* this returns the time offset only if the client and server are connected and have exchanged
* synchronization messages. Note the time offset may change over time as it is periodically
* updated; to receive updates as events, add a listener to the "time sync" event.
*
* @param inst NT instance handle.
* @return Time offset in microseconds (optional)
*/
public static native OptionalLong getServerTimeOffset(int inst);
/**
* Returns the current timestamp in microseconds.
*
* @return The current timestsamp in microseconds.
*/
public static native long now();
/**
* Starts logging entry changes to a DataLog.
*
* @param inst NT instance handle.
* @param log data log handle; lifetime must extend until StopEntryDataLog is called or the
* instance is destroyed
* @param prefix only store entries with names that start with this prefix; the prefix is not
* included in the data log entry name
* @param logPrefix prefix to add to data log entry names
* @return Data logger handle
*/
private static native int startEntryDataLog(int inst, long log, String prefix, String logPrefix);
/**
* Starts logging entry changes to a DataLog.
*
* @param inst NT instance handle.
* @param log data log object; lifetime must extend until StopEntryDataLog is called or the
* instance is destroyed
* @param prefix only store entries with names that start with this prefix; the prefix is not
* included in the data log entry name
* @param logPrefix prefix to add to data log entry names
* @return Data logger handle
*/
public static int startEntryDataLog(int inst, DataLog log, String prefix, String logPrefix) {
return startEntryDataLog(inst, log.getImpl(), prefix, logPrefix);
}
/**
* Stops logging entry changes to a DataLog.
*
* @param logger data logger handle
*/
public static native void stopEntryDataLog(int logger);
/**
* Starts logging connection changes to a DataLog.
*
* @param inst NT instance handle.
* @param log data log handle; lifetime must extend until StopConnectionDataLog is called or the
* instance is destroyed
* @param name data log entry name
* @return Data logger handle
*/
private static native int startConnectionDataLog(int inst, long log, String name);
/**
* Starts logging connection changes to a DataLog.
*
* @param inst NT instance handle.
* @param log data log object; lifetime must extend until StopConnectionDataLog is called or the
* instance is destroyed
* @param name data log entry name
* @return Data logger handle
*/
public static int startConnectionDataLog(int inst, DataLog log, String name) {
return startConnectionDataLog(inst, log.getImpl(), name);
}
/**
* Stops logging connection changes to a DataLog.
*
* @param logger data logger handle
*/
public static native void stopConnectionDataLog(int logger);
/**
* Add logger callback function. By default, log messages are sent to stderr; this function sends
* log messages with the specified levels to the provided callback function instead. The callback
* function will only be called for log messages with level greater than or equal to minLevel and
* less than or equal to maxLevel; messages outside this range will be silently ignored.
*
* @param poller Listener poller handle.
* @param minLevel minimum log level
* @param maxLevel maximum log level
* @return Listener handle
*/
public static native int addLogger(int poller, int minLevel, int maxLevel);
/** Utility class. */

View File

@@ -517,6 +517,12 @@ public interface GenericPublisher extends Publisher, Consumer<NetworkTableValue>
*/
boolean setDefaultBooleanArray(boolean[] defaultValue);
/**
* Sets the entry's value if it does not exist.
*
* @param defaultValue the default value to set
* @return False if the entry exists with a different type
*/
boolean setDefaultBooleanArray(Boolean[] defaultValue);
@@ -528,6 +534,12 @@ public interface GenericPublisher extends Publisher, Consumer<NetworkTableValue>
*/
boolean setDefaultIntegerArray(long[] defaultValue);
/**
* Sets the entry's value if it does not exist.
*
* @param defaultValue the default value to set
* @return False if the entry exists with a different type
*/
boolean setDefaultIntegerArray(Long[] defaultValue);
@@ -539,6 +551,12 @@ public interface GenericPublisher extends Publisher, Consumer<NetworkTableValue>
*/
boolean setDefaultFloatArray(float[] defaultValue);
/**
* Sets the entry's value if it does not exist.
*
* @param defaultValue the default value to set
* @return False if the entry exists with a different type
*/
boolean setDefaultFloatArray(Float[] defaultValue);
@@ -550,6 +568,12 @@ public interface GenericPublisher extends Publisher, Consumer<NetworkTableValue>
*/
boolean setDefaultDoubleArray(double[] defaultValue);
/**
* Sets the entry's value if it does not exist.
*
* @param defaultValue the default value to set
* @return False if the entry exists with a different type
*/
boolean setDefaultDoubleArray(Double[] defaultValue);

View File

@@ -1009,5 +1009,5 @@ public final class NetworkTableEntry implements Publisher, Subscriber {
}
private final Topic m_topic;
protected int m_handle;
private final int m_handle;
}

View File

@@ -68,6 +68,11 @@ public final class NetworkTableInstance implements AutoCloseable {
this.value = value;
}
/**
* Returns the network mode value.
*
* @return The network mode value.
*/
public int getValue() {
return value;
}

View File

@@ -40,5 +40,6 @@ public abstract class EntryBase implements Subscriber, Publisher {
return NetworkTablesJNI.getEntryLastChange(m_handle);
}
/** NetworkTables handle. */
protected int m_handle;
}

View File

@@ -7,16 +7,31 @@ package edu.wpi.first.networktables;
/** NetworkTables log message. */
@SuppressWarnings("MemberName")
public final class LogMessage {
/** Logging levels. */
/** Critical logging level. */
public static final int kCritical = 50;
/** Error logging level. */
public static final int kError = 40;
/** Warning log level. */
public static final int kWarning = 30;
/** Info log level. */
public static final int kInfo = 20;
/** Debug log level. */
public static final int kDebug = 10;
/** Debug log level 1. */
public static final int kDebug1 = 9;
/** Debug log level 2. */
public static final int kDebug2 = 8;
/** Debug log level 3. */
public static final int kDebug3 = 7;
/** Debug log level 4. */
public static final int kDebug4 = 6;
/** Log level of the message. */

View File

@@ -6,6 +6,7 @@ package edu.wpi.first.networktables;
import edu.wpi.first.util.sendable.SendableBuilder;
/** Helper class for building Sendable dashboard representations for NetworkTables. */
public interface NTSendableBuilder extends SendableBuilder {
/**
* Set the function that should be called to update the network table for things other than

View File

@@ -330,7 +330,7 @@ public final class NetworkTable {
* @return true if the table as a value assigned to the given key
*/
public boolean containsKey(String key) {
return !("".equals(key)) && getTopic(key).exists();
return !"".equals(key) && getTopic(key).exists();
}
/**

View File

@@ -12,6 +12,7 @@ package edu.wpi.first.networktables;
*/
@SuppressWarnings("MemberName")
public final class NetworkTableEvent {
/** NetworkTable event kind. */
public enum Kind {
/**
* Initial listener addition. Set this to receive immediate notification of matches to other
@@ -61,6 +62,11 @@ public final class NetworkTableEvent {
this.value = value;
}
/**
* Returns the NetworkTable event kind value.
*
* @return The NetworkTable event kind value.
*/
public int getValue() {
return value;
}

View File

@@ -39,10 +39,20 @@ public enum NetworkTableType {
m_valueStr = valueStr;
}
/**
* Returns the NetworkTable type value.
*
* @return The NetworkTable type value.
*/
public int getValue() {
return m_value;
}
/**
* Returns the NetworkTable type value as as string.
*
* @return The NetworkTable type value as a string.
*/
public String getValueStr() {
return m_valueStr;
}

View File

@@ -153,6 +153,11 @@ public final class ProtobufTopic<T> extends Topic {
false);
}
/**
* Returns the protobuf.
*
* @return The protobuf.
*/
public Protobuf<T, ?> getProto() {
return m_proto;
}

View File

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

View File

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

View File

@@ -153,6 +153,11 @@ public final class StructArrayTopic<T> extends Topic {
false);
}
/**
* Returns the struct.
*
* @return The struct.
*/
public Struct<T> getStruct() {
return m_struct;
}

View File

@@ -152,6 +152,11 @@ public final class StructTopic<T> extends Topic {
false);
}
/**
* Returns the struct.
*
* @return The struct.
*/
public Struct<T> getStruct() {
return m_struct;
}

View File

@@ -336,6 +336,9 @@ public class Topic {
return m_handle;
}
/** NetworkTables instance. */
protected NetworkTableInstance m_inst;
/** NetworkTables handle. */
protected int m_handle;
}

View File

@@ -46,10 +46,17 @@ bool LocalStorage::MultiSubscriberData::Matches(std::string_view name,
}
int LocalStorage::DataLoggerData::Start(TopicData* topic, int64_t time) {
std::string_view typeStr = topic->typeStr;
// NT and DataLog use different standard representations for int and int[]
if (typeStr == "int") {
typeStr = "int64";
} else if (typeStr == "int[]") {
typeStr = "int64[]";
}
return log.Start(fmt::format("{}{}", logPrefix,
wpi::drop_front(topic->name, prefix.size())),
topic->typeStr == "int" ? "int64" : topic->typeStr,
DataLoggerEntry::MakeMetadata(topic->propertiesStr), time);
typeStr, DataLoggerEntry::MakeMetadata(topic->propertiesStr),
time);
}
void LocalStorage::DataLoggerEntry::Append(const Value& v) {
@@ -604,7 +611,7 @@ LocalStorage::SubscriberData* LocalStorage::Impl::AddLocalSubscriber(
"published as '{}')",
topic->name, config.typeStr, topic->typeStr);
}
if (m_network) {
if (m_network && !subscriber->config.hidden) {
DEBUG4("-> NetworkSubscribe({})", topic->name);
m_network->Subscribe(subscriber->handle, {{topic->name}}, config);
}
@@ -633,7 +640,7 @@ LocalStorage::Impl::RemoveLocalSubscriber(NT_Subscriber subHandle) {
listener.getSecond()->subscriber = nullptr;
}
}
if (m_network) {
if (m_network && !subscriber->config.hidden) {
m_network->Unsubscribe(subscriber->handle);
}
}
@@ -669,7 +676,7 @@ LocalStorage::MultiSubscriberData* LocalStorage::Impl::AddMultiSubscriber(
}
}
}
if (m_network) {
if (m_network && !subscriber->options.hidden) {
DEBUG4("-> NetworkSubscribe");
m_network->Subscribe(subscriber->handle, subscriber->prefixes,
subscriber->options);
@@ -689,7 +696,7 @@ LocalStorage::Impl::RemoveMultiSubscriber(NT_MultiSubscriber subHandle) {
listener.getSecond()->multiSubscriber = nullptr;
}
}
if (m_network) {
if (m_network && !subscriber->options.hidden) {
m_network->Unsubscribe(subscriber->handle);
}
}
@@ -1121,12 +1128,16 @@ void LocalStorage::Impl::StartNetwork(net::NetworkInterface* network) {
}
}
for (auto&& subscriber : m_subscribers) {
network->Subscribe(subscriber->handle, {{subscriber->topic->name}},
subscriber->config);
if (!subscriber->config.hidden) {
network->Subscribe(subscriber->handle, {{subscriber->topic->name}},
subscriber->config);
}
}
for (auto&& subscriber : m_multiSubscribers) {
network->Subscribe(subscriber->handle, subscriber->prefixes,
subscriber->options);
if (!subscriber->options.hidden) {
network->Subscribe(subscriber->handle, subscriber->prefixes,
subscriber->options);
}
}
}

View File

@@ -254,13 +254,13 @@ class LocalStorage final : public net::ILocalStorage {
wpi::SmallVectorImpl<typename TypeInfo<T>::SmallElem>& buf,
typename TypeInfo<T>::View defaultValue);
std::vector<Value> ReadQueueValue(NT_Handle subentry) {
std::vector<Value> ReadQueueValue(NT_Handle subentry, unsigned int types) {
std::scoped_lock lock{m_mutex};
auto subscriber = m_impl.GetSubEntry(subentry);
if (!subscriber) {
return {};
}
return subscriber->pollStorage.ReadValue();
return subscriber->pollStorage.ReadValue(types);
}
template <ValidType T>

View File

@@ -362,6 +362,7 @@ void NetworkClient::HandleLocal() {
}
void NetworkClient::TcpConnected(uv::Tcp& tcp) {
tcp.SetLogger(&m_logger);
tcp.SetNoDelay(true);
// Start the WS client
if (m_logger.min_level() >= wpi::WPI_LOG_DEBUG4) {
@@ -401,9 +402,8 @@ void NetworkClient::WsConnected(wpi::WebSocket& ws, uv::Tcp& tcp,
INFO("CONNECTED NT4 to {} port {}", connInfo.remote_ip, connInfo.remote_port);
m_connHandle = m_connList.AddConnection(connInfo);
m_wire =
std::make_shared<net::WebSocketConnection>(ws, connInfo.protocol_version);
m_wire->Start();
m_wire = std::make_shared<net::WebSocketConnection>(
ws, connInfo.protocol_version, m_logger);
m_clientImpl = std::make_unique<net::ClientImpl>(
m_loop.Now().count(), m_inst, *m_wire, m_logger, m_timeSyncUpdated,
[this](uint32_t repeatMs) {

View File

@@ -242,7 +242,7 @@ void NetworkServer::ServerConnection4::ProcessWsUpgrade() {
m_info.protocol_version =
protocol == "v4.1.networktables.first.wpi.edu" ? 0x0401 : 0x0400;
m_wire = std::make_shared<net::WebSocketConnection>(
*m_websocket, m_info.protocol_version);
*m_websocket, m_info.protocol_version, m_logger);
if (protocol == "rtt.networktables.first.wpi.edu") {
INFO("CONNECTED RTT client (from {})", m_connInfo);
@@ -281,7 +281,6 @@ void NetworkServer::ServerConnection4::ProcessWsUpgrade() {
INFO("CONNECTED NT4 client '{}' (from {})", dedupName, m_connInfo);
m_info.remote_id = dedupName;
m_server.AddConnection(this, m_info);
m_wire->Start();
m_websocket->closed.connect([this](uint16_t, std::string_view reason) {
auto realReason = m_wire->GetDisconnectReason();
INFO("DISCONNECTED NT4 client '{}' (from {}): {}", m_info.remote_id,
@@ -500,6 +499,7 @@ void NetworkServer::Init() {
if (!tcp) {
return;
}
tcp->SetLogger(&m_logger);
tcp->error.connect([logger = &m_logger](uv::Error err) {
WPI_INFO(*logger, "NT4 socket error: {}", err.str());
});

View File

@@ -6,10 +6,13 @@
using namespace nt;
std::vector<Value> ValueCircularBuffer::ReadValue() {
std::vector<Value> ValueCircularBuffer::ReadValue(unsigned int types) {
std::vector<Value> rv;
rv.reserve(m_storage.size());
for (auto&& val : m_storage) {
if (types != 0 && (types & val.type()) == 0) {
continue;
}
rv.emplace_back(std::move(val));
}
m_storage.reset();

View File

@@ -24,7 +24,7 @@ class ValueCircularBuffer {
m_storage.emplace_back(std::forward<Args...>(args...));
}
std::vector<Value> ReadValue();
std::vector<Value> ReadValue(unsigned int types);
template <ValidType T>
std::vector<Timestamped<typename TypeInfo<T>::Value>> Read();

View File

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

View File

@@ -67,26 +67,28 @@ void ClientImpl::ProcessIncomingBinary(uint64_t curTimeMs,
DEBUG4("BinaryMessage({})", id);
// handle RTT ping response (only use first one)
if (!m_haveTimeOffset && id == -1) {
if (!value.IsInteger()) {
WARN("RTT ping response with non-integer type {}",
static_cast<int>(value.type()));
continue;
}
DEBUG4("RTT ping response time {} value {}", value.time(),
value.GetInteger());
if (m_wire.GetVersion() < 0x0401) {
m_pongTimeMs = curTimeMs;
}
int64_t now = wpi::Now();
int64_t rtt2 = (now - value.GetInteger()) / 2;
if (rtt2 < m_rtt2Us) {
m_rtt2Us = rtt2;
int64_t serverTimeOffsetUs = value.server_time() + rtt2 - now;
DEBUG3("Time offset: {}", serverTimeOffsetUs);
m_outgoing.SetTimeOffset(serverTimeOffsetUs);
m_haveTimeOffset = true;
m_timeSyncUpdated(serverTimeOffsetUs, m_rtt2Us, true);
if (id == -1) {
if (!m_haveTimeOffset) {
if (!value.IsInteger()) {
WARN("RTT ping response with non-integer type {}",
static_cast<int>(value.type()));
continue;
}
DEBUG4("RTT ping response time {} value {}", value.time(),
value.GetInteger());
if (m_wire.GetVersion() < 0x0401) {
m_pongTimeMs = curTimeMs;
}
int64_t now = wpi::Now();
int64_t rtt2 = (now - value.GetInteger()) / 2;
if (rtt2 < m_rtt2Us) {
m_rtt2Us = rtt2;
int64_t serverTimeOffsetUs = value.server_time() + rtt2 - now;
DEBUG3("Time offset: {}", serverTimeOffsetUs);
m_outgoing.SetTimeOffset(serverTimeOffsetUs);
m_haveTimeOffset = true;
m_timeSyncUpdated(serverTimeOffsetUs, m_rtt2Us, true);
}
}
continue;
}

View File

@@ -12,14 +12,15 @@ bool NetworkPing::Send(uint64_t curTimeMs) {
if (curTimeMs < m_nextPingTimeMs) {
return true;
}
// if we didn't receive a timely response to our last ping, disconnect
uint64_t lastPing = m_wire.GetLastPingResponse();
// if we haven't received data in a while, disconnect
// (we should at least be getting PONG responses)
uint64_t lastData = m_wire.GetLastReceivedTime();
// DEBUG4("WS ping: lastPing={} curTime={} pongTimeMs={}\n", lastPing,
// curTimeMs, m_pongTimeMs);
if (lastPing == 0) {
lastPing = m_pongTimeMs;
if (lastData == 0) {
lastData = m_pongTimeMs;
}
if (m_pongTimeMs != 0 && curTimeMs > (lastPing + kPingTimeoutMs)) {
if (m_pongTimeMs != 0 && curTimeMs > (lastData + kPingTimeoutMs)) {
m_wire.Disconnect("connection timed out");
return false;
}

View File

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

View File

@@ -8,6 +8,7 @@
#include <span>
#include <wpi/Endian.h>
#include <wpi/Logger.h>
#include <wpi/SpanExtras.h>
#include <wpi/raw_ostream.h>
#include <wpi/timestamp.h>
@@ -52,7 +53,11 @@ class WebSocketConnection::Stream final : public wpi::raw_ostream {
void WebSocketConnection::Stream::write_impl(const char* data, size_t len) {
if (data == m_conn.m_bufs.back().base) {
// flush_nonempty() case
size_t amt = len - m_conn.m_bufs.back().len;
WPI_DEBUG4(m_conn.m_logger, "conn: writing {} bytes (nonempty)", amt);
m_conn.m_bufs.back().len = len;
m_conn.m_framePos += amt;
m_conn.m_written += amt;
if (!m_disableAlloc) {
#ifdef NT_ENABLE_WS_FRAG
m_conn.m_frames.back().opcode &= ~wpi::WebSocket::kFlagFin;
@@ -74,6 +79,7 @@ void WebSocketConnection::Stream::write_impl(const char* data, size_t len) {
size_t amt = (std::min)(static_cast<int>(kAllocSize - buf.len),
static_cast<int>(len));
if (amt > 0) {
WPI_DEBUG4(m_conn.m_logger, "conn: writing {} bytes", amt);
std::memcpy(buf.base + buf.len, data, amt);
buf.len += amt;
m_conn.m_framePos += amt;
@@ -101,8 +107,9 @@ void WebSocketConnection::Stream::write_impl(const char* data, size_t len) {
}
WebSocketConnection::WebSocketConnection(wpi::WebSocket& ws,
unsigned int version)
: m_ws{ws}, m_version{version} {}
unsigned int version,
wpi::Logger& logger)
: m_ws{ws}, m_logger{logger}, m_version{version} {}
WebSocketConnection::~WebSocketConnection() {
for (auto&& buf : m_bufs) {
@@ -113,19 +120,8 @@ WebSocketConnection::~WebSocketConnection() {
}
}
void WebSocketConnection::Start() {
m_ws.pong.connect([selfweak = weak_from_this()](auto data) {
if (data.size() != 8) {
return;
}
if (auto self = selfweak.lock()) {
self->m_lastPingResponse =
wpi::support::endian::read64<wpi::support::native>(data.data());
}
});
}
void WebSocketConnection::SendPing(uint64_t time) {
WPI_DEBUG4(m_logger, "conn: sending ping {}", time);
auto buf = AllocBuf();
buf.len = 8;
wpi::support::endian::write64<wpi::support::native>(buf.base, time);
@@ -142,6 +138,8 @@ void WebSocketConnection::SendPing(uint64_t time) {
}
void WebSocketConnection::StartFrame(uint8_t opcode) {
WPI_DEBUG4(m_logger, "conn: starting frame {}",
static_cast<unsigned int>(opcode));
m_frames.emplace_back(opcode, m_bufs.size(), m_bufs.size() + 1);
m_bufs.emplace_back(AllocBuf());
m_bufs.back().len = 0;
@@ -177,6 +175,7 @@ int WebSocketConnection::Write(
if (kind == kText) {
os << (first ? '[' : ',');
}
WPI_DEBUG4(m_logger, "writing");
writer(os);
}
++m_frames.back().count;
@@ -188,6 +187,7 @@ int WebSocketConnection::Write(
}
int WebSocketConnection::Flush() {
WPI_DEBUG4(m_logger, "conn: flushing");
m_lastFlushTime = wpi::Now();
if (m_state == kEmpty) {
return 0;
@@ -252,6 +252,7 @@ void WebSocketConnection::Send(
os << ']';
}
wpi::WebSocket::Frame frame{opcode, os.bufs()};
WPI_DEBUG4(m_logger, "Send({})", static_cast<uint8_t>(opcode));
m_ws.SendFrames({{frame}}, [selfweak = weak_from_this()](auto bufs, auto) {
if (auto self = selfweak.lock()) {
self->ReleaseBufs(bufs);
@@ -265,7 +266,7 @@ void WebSocketConnection::Send(
void WebSocketConnection::Disconnect(std::string_view reason) {
m_reason = reason;
m_ws.Fail(1005, reason);
m_ws.Fail(1001, reason);
}
wpi::uv::Buffer WebSocketConnection::AllocBuf() {

View File

@@ -15,19 +15,22 @@
#include "WireConnection.h"
namespace wpi {
class Logger;
} // namespace wpi
namespace nt::net {
class WebSocketConnection final
: public WireConnection,
public std::enable_shared_from_this<WebSocketConnection> {
public:
WebSocketConnection(wpi::WebSocket& ws, unsigned int version);
WebSocketConnection(wpi::WebSocket& ws, unsigned int version,
wpi::Logger& logger);
~WebSocketConnection() override;
WebSocketConnection(const WebSocketConnection&) = delete;
WebSocketConnection& operator=(const WebSocketConnection&) = delete;
void Start();
unsigned int GetVersion() const final { return m_version; }
void SendPing(uint64_t time) final;
@@ -51,7 +54,9 @@ class WebSocketConnection final
uint64_t GetLastFlushTime() const final { return m_lastFlushTime; }
uint64_t GetLastPingResponse() const final { return m_lastPingResponse; }
uint64_t GetLastReceivedTime() const final {
return m_ws.GetLastReceivedTime();
}
void Disconnect(std::string_view reason) final;
@@ -70,6 +75,7 @@ class WebSocketConnection final
void ReleaseBufs(std::span<wpi::uv::Buffer> bufs);
wpi::WebSocket& m_ws;
wpi::Logger& m_logger;
class Stream;
@@ -92,7 +98,6 @@ class WebSocketConnection final
State m_state = kEmpty;
std::string m_reason;
uint64_t m_lastFlushTime = 0;
uint64_t m_lastPingResponse = 0;
unsigned int m_version;
};

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