Compare commits

...

212 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
Tyler Veness
4809f3d0fc [apriltag] Add 2024 AprilTag locations (#6168) 2024-01-06 12:50:27 -08:00
Peter Johnson
dd90965362 [wpiutil] Fix RawFrame.setInfo() NPE (#6167) 2024-01-06 12:50:11 -08:00
sciencewhiz
8659372d08 [fieldImages] Add 2024 field image (#6166) 2024-01-06 12:06:15 -08:00
Eli Barnett
a2e4d0b15d [docs] Fix docs for SysID routine (#6164) 2024-01-05 22:05:09 -08:00
Tyler Veness
0a46a3a618 [wpilib] Make ADXL345 default I2C address public (#6163)
pybind needs it.
2024-01-05 16:59:30 -08:00
Peter Johnson
7c26bc70ab [sysid] Load DataLog files directly for analysis (#6103)
Co-authored-by: Oblarg <emichaelbrnett@gmail.com>
2024-01-05 16:24:31 -08:00
Tyler Veness
f94e3d81b9 [docs] Fix SysId routine JavaDoc warnings (#6159) 2024-01-05 16:03:52 -08:00
Tyler Veness
6bed82a18e [wpilibc] Clean up C++ SysId routine (#6160) 2024-01-05 15:22:52 -08:00
Starlight220
4595f84719 [wpilib] Report LiveWindow-enabled-in-test (#6158) 2024-01-05 11:57:14 -08:00
Eli Barnett
707cb06105 [wpilib] Add SysIdRoutine logging utility and command factory (#6033)
Co-authored-by: Drew Williams <williams.r.drew@gmail.com>
Co-authored-by: Peter Johnson <johnson.peter@gmail.com>
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2024-01-05 11:50:23 -08:00
Starlight220
3e40b9e5da [wpilib] Correct SmartDashboard usage reporting (#6157) 2024-01-05 11:18:29 -08:00
Tyler Veness
106518c3f8 [docs] Fix wpilibj JavaDoc warnings (#6154) 2024-01-05 07:35:59 -08:00
Tyler Veness
19cb2a8eb4 [wpilibj] Make class variables private to match C++ (#6153) 2024-01-05 00:59:34 -08:00
m10653
13f4460e00 [docs] Add missing docs to enum fields (NFC) (#6150)
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2024-01-04 21:36:26 -08:00
Tyler Veness
4210f5635d [docs] Fix warnings about undocumented default constructors (#6151) 2024-01-04 13:57:21 -08:00
Peter Johnson
0f060afb55 [ntcore] Disable WebSocket fragmentation (#6149)
There seems to be a latent bug in the fragmentation code that we've not
been able to isolate.
2024-01-04 12:09:31 -08:00
Tyler Veness
f29a7d2e50 [docs] Add missing JavaDocs (#6146) 2024-01-04 08:38:06 -08:00
Tyler Veness
6e58db398d [commands] Make Java fields private (#6148)
They were already private in C++ and probably shouldn't be exposed. This
also means we don't need to write redundant documentation for them.
2024-01-04 00:57:52 -08:00
Gold856
4ac0720385 [build] Clean up CMake files (#6141) 2024-01-04 00:47:47 -08:00
Tyler Veness
44db3e0ac0 [sysid] Make constexpr variables outside class scope inline (#6145) 2024-01-03 14:27:51 -08:00
Peter Johnson
73c7d87db7 [glass] NTStringChooser: Properly set retained (#6144) 2024-01-03 09:38:10 -08:00
David Vo
25636b712f [build] Remove unnecessary native dependencies in wpilibjExamples (#6143)
These dependencies don't have any JNI, so there's no need to link them.
2024-01-02 20:14:37 -08:00
m10653
01fb98baaa [docs] Add Missing JNI docs from C++ (NFC) (#6139) 2024-01-02 20:13:46 -08:00
David Vo
5c424248c4 [wpilibj] Remove unused AnalogTriggerException (#6142) 2024-01-02 20:10:51 -08:00
Tyler Veness
c486972c55 [wpimath] Make ExponentialProfile.State mutable (#6138)
It was already mutable in the C++ ExponentialProfile and
TrapezoidProfile in both languages.
2024-01-02 11:39:55 -08:00
David Vo
783acb9b72 [wpilibj] Store long preferences as integers (#6136) 2024-01-01 23:38:02 -08:00
m10653
99ab836894 [wpiutil] Add missing JavaDocs (NFC) (#6132) 2024-01-01 23:37:39 -08:00
Tyler Veness
ad0859a8c9 [docs] Add missing JavaDocs (#6125) 2024-01-01 22:56:23 -08:00
Gold856
5579219716 [docs] Exclude quickbuf files and proto/struct packages from doclint (#6128) 2024-01-01 21:23:09 -08:00
Tyler Veness
98f06911c7 [sysid] Use eigenvector component instead of eigenvalue for fit quality check (#6131)
They're usually close, but this is a better metric.
2024-01-01 21:22:34 -08:00
Braykoff
e1d49b975c [wpimath] Add LinearFilter reset() overload to initialize input and output buffers (#6133) 2024-01-01 21:20:53 -08:00
m10653
8a0bf2b7a4 [hal] Add CANAPITypes to java (#6121) 2024-01-01 16:51:17 -08:00
Tyler Veness
91d8837c11 [wpilib] Make protected fields in accelerometers/gyros private (#6134)
This avoids needing add redundant JavaDocs to them, and better reflects
how we design our modern classes (the classes modified here were around
with minimal changes since 2008 or so).
2024-01-01 16:49:50 -08:00
Tyler Veness
e7c9f27683 [wpilib] Add functional interface equivalents to MotorController (#6053)
This does not deprecate any current functionality, but prepares the way for future deprecation.

The drive classes now accept void(double) functions, which makes them more flexible.

The C++ API ended up a bit more verbose, but the Java API is really concise with method references, which is >80% of our userbase. For example:

`DifferentialDrive drive = new DifferentialDrive(m_leftMotor::set, m_rightMotor::set);`

Lambdas can be passed to interoperate with vendor motor controller APIs that don't have e.g., set(double), so CTRE doesn't have to maintain their WPI_ classes anymore.

MotorControllerGroup was replaced with PWMMotorController.addFollower() for PWM motor controllers. Users of CAN motor controllers should use their vendor's follower functionality.
2024-01-01 13:37:51 -08:00
m10653
8aca706217 [glass] Add type information to SmartDashboard menu (#6117) 2024-01-01 11:58:13 -08:00
sciencewhiz
7d3e4ddba9 [docs] Add warning about using user button to docs (NFC) (#6129) 2024-01-01 11:53:21 -08:00
Tyler Veness
ec3cb3dcba [build] Disable clang-tidy warning about test case names (#6127)
These are the warnings being disabled:
```
== clang-tidy /__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp ==
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:51:16: warning: avoid using "_" in test name "NonInvertibleA_ABQR" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
   51 | TEST(DARETest, NonInvertibleA_ABQR) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:67:16: warning: avoid using "_" in test name "NonInvertibleA_ABQRN" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
   67 | TEST(DARETest, NonInvertibleA_ABQRN) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:89:16: warning: avoid using "_" in test name "InvertibleA_ABQR" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
   89 | TEST(DARETest, InvertibleA_ABQR) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:101:16: warning: avoid using "_" in test name "InvertibleA_ABQRN" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  101 | TEST(DARETest, InvertibleA_ABQRN) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:118:16: warning: avoid using "_" in test name "FirstGeneralizedEigenvalueOfSTIsStable_ABQR" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  118 | TEST(DARETest, FirstGeneralizedEigenvalueOfSTIsStable_ABQR) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:132:16: warning: avoid using "_" in test name "FirstGeneralizedEigenvalueOfSTIsStable_ABQRN" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  132 | TEST(DARETest, FirstGeneralizedEigenvalueOfSTIsStable_ABQRN) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:151:16: warning: avoid using "_" in test name "IdentitySystem_ABQR" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  151 | TEST(DARETest, IdentitySystem_ABQR) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:163:16: warning: avoid using "_" in test name "IdentitySystem_ABQRN" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  163 | TEST(DARETest, IdentitySystem_ABQRN) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:176:16: warning: avoid using "_" in test name "MoreInputsThanStates_ABQR" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  176 | TEST(DARETest, MoreInputsThanStates_ABQR) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:188:16: warning: avoid using "_" in test name "MoreInputsThanStates_ABQRN" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  188 | TEST(DARETest, MoreInputsThanStates_ABQRN) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:201:16: warning: avoid using "_" in test name "QNotSymmetricPositiveSemidefinite_ABQR" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  201 | TEST(DARETest, QNotSymmetricPositiveSemidefinite_ABQR) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:210:16: warning: avoid using "_" in test name "QNotSymmetricPositiveSemidefinite_ABQRN" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  210 | TEST(DARETest, QNotSymmetricPositiveSemidefinite_ABQRN) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:220:16: warning: avoid using "_" in test name "RNotSymmetricPositiveDefinite_ABQR" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  220 | TEST(DARETest, RNotSymmetricPositiveDefinite_ABQR) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:232:16: warning: avoid using "_" in test name "RNotSymmetricPositiveDefinite_ABQRN" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  232 | TEST(DARETest, RNotSymmetricPositiveDefinite_ABQRN) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:245:16: warning: avoid using "_" in test name "ABNotStabilizable_ABQR" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  245 | TEST(DARETest, ABNotStabilizable_ABQR) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:254:16: warning: avoid using "_" in test name "ABNotStabilizable_ABQRN" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  254 | TEST(DARETest, ABNotStabilizable_ABQRN) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:264:16: warning: avoid using "_" in test name "ACNotDetectable_ABQR" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  264 | TEST(DARETest, ACNotDetectable_ABQR) {
      |                ^
/__w/allwpilib/allwpilib/wpimath/src/test/native/cpp/DARETest.cpp:273:16: warning: avoid using "_" in test name "ACNotDetectable_ABQRN" according to Googletest FAQ [google-readability-avoid-underscore-in-googletest-name]
  273 | TEST(DARETest, ACNotDetectable_ABQRN) {
      |                ^
```
2023-12-31 23:25:05 -08:00
sciencewhiz
495585b25d [examples] Update april tag family to 36h11 (#6126)
Changes size to 6.5 inches per https://www.firstinspires.org/robotics/frc/blog/2023-technology-updates-past-present-future-and-beyond
Uses 7 error bit correction as recommended in PhotonVision docs.
2023-12-31 22:46:31 -08:00
Thad House
f9aabc5ab2 [wpilib] Throw early when EventLoop is modified while running (#6115) 2023-12-31 22:45:10 -08:00
m10653
c16946c0ec [hal] Add CANJNI docs (NFC) (#6120) 2023-12-31 22:44:27 -08:00
sciencewhiz
b7f4eb2811 [doc] Update maven artifacts for units and apriltags (NFC) (#6123) 2023-12-31 22:43:43 -08:00
sciencewhiz
f419a62b38 [doc] Update maintainers.md (NFC) (#6124)
Update third party artifacts
Update installer publishing
Fixes #6106
2023-12-31 22:43:20 -08:00
Joseph Eng
938bf45fd9 [wpiutil] Remove type param from ProtobufSerializable and StructSerializable (#6122) 2023-12-31 14:36:11 -08:00
Tyler Veness
c34debe012 [docs] Link to external OpenCV docs (#6119)
This avoids thousands of warnings from JavaDoc 17 parsing the OpenCV
source.
2023-12-30 21:09:07 -06:00
David Vo
07183765de [hal] Fix formatting of HAL_ENUM enums (NFC) (#6114) 2023-12-30 09:22:46 -06:00
Thad House
af46034b7f [wpilib] Document only first party controllers are guaranteed to have correct mapping (#6112) 2023-12-30 09:16:59 -06:00
Thad House
636ef58d94 [hal] Properly error check readCANStreamSession (#6108) 2023-12-29 23:57:00 -06:00
Thad House
cc631d2a69 [build] Fix generated source set location in the HAL (#6113) 2023-12-29 23:56:38 -06:00
Tyler Veness
09f76b32c2 [wpimath] Compile with UTF-8 encoding (#6111)
This allows using Greek letters in variable names, which is helpful to
make implemented equations follow their source paper more closely.
2023-12-29 23:56:06 -06:00
Tyler Veness
47c5fd8620 [sysid] Check data quality before OLS (#6110) 2023-12-29 23:31:27 -06:00
Thad House
24a76be694 [hal] Add method to detect if the CAN Stream has overflowed (#6105) 2023-12-29 09:10:48 -08:00
Thad House
9333951736 [hal] Allocate CANStreamMessage in JNI if null (#6107) 2023-12-29 00:50:57 -06:00
Peter Johnson
6a2d3c30a6 [wpiutil] Struct: Add info template parameter pack (#6086)
This allows using Struct in a dynamically typed environment by passing
additional information to the Struct serialization functions.
2023-12-27 09:52:18 -06:00
Tyler Veness
e07de37e64 [commands] Mark ParallelDeadlineGroup.setDeadline() final (#6102)
Suppress this-escape warning.
2023-12-27 09:51:16 -06:00
Gold856
141241d2d6 [wpilib] Fix usage reporting for static classes (#6090) 2023-12-26 23:16:55 -06:00
Tyler Veness
f2c2bab7dc [sysid] Fix adjusted R² calculation (#6101)
It hardcoded p to 2.
2023-12-26 22:06:10 -06:00
Peter Johnson
5659038443 [wpiutil,cscore,apriltag] Fix RawFrame (#6098) 2023-12-26 22:05:02 -06:00
Joseph Eng
8aeee03626 [commands] Improve error message when composing commands twice in same composition (#6091)
Also disallow deadline command from also appearing in other commands.
2023-12-26 18:07:16 -08:00
Peter Johnson
55508706ff [wpiutil,cscore] Move VideoMode.PixelFormat to wpiutil (#6097)
This tracks the RawFrame move.
2023-12-26 16:47:17 -06:00
Bryce Roethel
ab78b930e9 [wpilib] ADIS16470: Add access to all 3 axes (#6074) 2023-12-26 13:39:37 -08:00
Tyler Veness
795d4be9fd [wpilib] Fix precision issue in Color round-and-clamp (#6100) 2023-12-26 13:38:15 -08:00
Joseph Eng
7aa9ad44b8 [commands] Deprecate C++ TransferOwnership() (#6095)
It has been completely replaced with ToPtr().
2023-12-26 15:14:34 -06:00
David Vo
92c81d0791 [ci] Update pregenerate workflow to actions/checkout@v4 (#6094) 2023-12-25 08:03:03 -06:00
David Vo
1ce617be07 [ci] Update artifact actions to v4 (#6092) 2023-12-24 11:51:53 -06:00
sciencewhiz
2441b57156 [wpilib] Add PWMSparkFlex MotorController (#6089) 2023-12-24 01:47:38 -06:00
Peter Johnson
21d1972d7a [wpiutil] DataLog: Ensure file is written on shutdown (#6087)
Previously the thread could end without the file being written.
2023-12-23 15:40:51 -06:00
Peter Johnson
c29e8c66cf [wpiutil] DataLog: Fix UB in AppendImpl (#6088) 2023-12-23 15:39:39 -06:00
truher
ab309e34ef [glass] Fix order of loading window settings (#6056) 2023-12-23 13:12:09 -08:00
Tyler Veness
22a322c9f3 [wpimath] Report error on negative PID gains (#6055)
Defaults PID gains to zero if any are invalid.
2023-12-23 12:15:29 -08:00
Asa Anderson
1dba26c937 [wpilib] Add method to get breaker fault at a specific channel in PowerDistribution[Sticky]Faults (#5521)
Co-authored-by: Ryan Blue <ryanzblue@gmail.com>
2023-12-23 12:14:13 -08:00
Ryan Blue
ef1cb3f41e [commands] Fix compose-while-scheduled issue and test all compositions (#5581) 2023-12-23 12:12:13 -08:00
Peter Johnson
aeb1a4aa33 [wpiutil] Add serializable marker interfaces (#6060) 2023-12-23 08:20:26 -08:00
NC GEARS FRC 1918
c1178d5add [wpilib] Add StadiaController and command wrapper (#6083) 2023-12-23 08:15:05 -08:00
ncorrea210
4e4a468d4d [wpimath] Make feedforward classes throw exceptions for negative Kv or Ka (#6084) 2023-12-23 08:12:46 -08:00
swirl
d1793f077d [build] cmake: Add NO_WERROR option to disable -Werror (#6071) 2023-12-22 14:38:38 -06:00
m10653
43fb6e9f87 [glass] Add Profiled PID controller support & IZone Support (#5959) 2023-12-22 11:29:25 -08:00
Drew Williams
bcef6c5398 [apriltag] Fix Java generation functions (#6063) 2023-12-22 11:03:02 -08:00
Ryan Blue
4059e0cd9f [hal,wpilib] Add function to control "Radio" LED (#6073) 2023-12-22 10:57:52 -08:00
Ryan Blue
0b2cfb3abc [dlt] Change datalogtool default folder to logs folder (#6079)
Also fix straggling documentation.
2023-12-22 10:54:17 -08:00
Tyler Veness
df5e439b0c [wpilib] PS4Controller: enable usage reporting (#6081) 2023-12-22 12:33:03 -06:00
Thad House
0ff7478968 [cscore] Fix RawFrame class not being loaded in JNI (#6077) 2023-12-21 19:58:12 -06:00
sciencewhiz
6f23d32fe1 [wpilib] AddressableLED: Update warning about single driver (NFC) (#6069)
Say that multiple strips can be used in series.
2023-12-20 22:32:16 -06:00
Isaac Turner
35a1c52788 [build] Upgrade quickbuf to 1.3.3 (#6072) 2023-12-20 22:30:45 -06:00
Tyler Veness
e4e2bafdb1 [sysid] Document timestamp units (#6065) 2023-12-19 22:37:41 -08:00
Peter Johnson
3d201c71f7 [ntcore] Fix overlapping subscriber handling (#6067) 2023-12-19 22:37:16 -08:00
Peter Johnson
f02984159f [glass] Check for null entries when updating struct/proto (#6059) 2023-12-18 11:31:08 -08:00
Eli Barnett
a004c9e05f [commands] SubsystemBase: allow setting name in constructor (#6052) 2023-12-16 11:05:53 -08:00
Tyler Veness
0b4c6a1546 [wpimath] Add more docs to SimulatedAnnealing (NFC) (#6054) 2023-12-16 11:03:54 -08:00
Tyler Veness
ab15dae887 [wpilib] ArcadeDrive: Fix max output handling (#6051) 2023-12-15 09:18:45 -08:00
1034 changed files with 34205 additions and 11948 deletions

View File

@@ -145,6 +145,8 @@ LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
Macros:
- 'HAL_ENUM(name)=enum name'
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never

View File

@@ -49,7 +49,6 @@ Checks:
google-build-namespaces,
google-explicit-constructor,
google-global-names-in-headers,
google-readability-avoid-underscore-in-googletest-name,
google-readability-casting,
google-runtime-operator,
misc-definitions-in-headers,

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

@@ -32,7 +32,7 @@ jobs:
- name: Install QuickBuffers (Linux)
if: runner.os == 'Linux'
run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.2/protoc-gen-quickbuf_1.3.2_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.2_amd64.deb
run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf_1.3.3_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.3_amd64.deb
- name: Install opencv (macOS)
run: brew install opencv protobuf@3 ninja
@@ -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

@@ -54,7 +54,7 @@ jobs:
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
- name: Check free disk space
run: df .
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: build/allOutputs
@@ -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
@@ -159,7 +159,7 @@ jobs:
- name: Check disk free space (Windows)
run: wmic logicaldisk get caption, freespace
if: matrix.os == 'windows-2022'
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: ${{ matrix.build-dir }}/${{ matrix.outputs }}
@@ -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')
@@ -183,7 +183,7 @@ jobs:
env:
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: Documentation
path: docs/build/outputs
@@ -212,7 +212,7 @@ jobs:
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
with:
repository: wpilibsuite/build-tools
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
if: |
github.repository_owner == 'wpilibsuite' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
@@ -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'))
@@ -255,7 +255,7 @@ jobs:
RUN_AZURE_ARTIFACTORY_RELEASE: "TRUE"
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: |
github.repository_owner == 'wpilibsuite' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))

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
@@ -35,7 +35,7 @@ jobs:
- name: Generate diff
run: git diff HEAD > wpiformat-fixes.patch
if: ${{ failure() }}
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: wpiformat fixes
path: wpiformat-fixes.patch
@@ -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

@@ -15,26 +15,23 @@ jobs:
name: "Update"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Fetch all history and metadata
run: |
git fetch --prune --unshallow
git checkout -b pr
git branch -f main origin/main
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python 3.9
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Install jinja
run: python -m pip install jinja2
- name: Install protobuf dependencies
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler && wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.2/protoc-gen-quickbuf-1.3.2-linux-x86_64.exe && chmod +x protoc-gen-quickbuf-1.3.2-linux-x86_64.exe
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler && wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf-1.3.3-linux-x86_64.exe && chmod +x protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
- name: Run hal
run: ./hal/generate_usage_reporting.py
- name: Run ntcore
run: ./ntcore/generate_topics.py
- name: Run wpimath
run: ./wpimath/generate_numbers.py && ./wpimath/generate_quickbuf.py protoc protoc-gen-quickbuf-1.3.2-linux-x86_64.exe
run: ./wpimath/generate_numbers.py && ./wpimath/generate_quickbuf.py protoc protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
- name: Add untracked files to index so they count as changes
run: git add -A
- name: Check output
@@ -42,7 +39,7 @@ jobs:
- name: Generate diff
run: git diff HEAD > pregenerated-files-fixes.patch
if: ${{ failure() }}
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: pregenerated-files-fixes
path: pregenerated-files-fixes.patch

View File

@@ -33,7 +33,7 @@ jobs:
- name: Install QuickBuffers
if: runner.os == 'Linux'
run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.2/protoc-gen-quickbuf_1.3.2_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.2_amd64.deb
run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf_1.3.3_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.3_amd64.deb
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.3

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

@@ -54,7 +54,7 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir)
if("${isSystemDir}" STREQUAL "-1")
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
endif("${isSystemDir}" STREQUAL "-1")
endif()
# Options for building certain parts of the repo. Everything is built by default.
option(BUILD_SHARED_LIBS "Build with shared libs (needed for JNI)" ON)
@@ -82,6 +82,9 @@ option(USE_SYSTEM_EIGEN "Use system eigen" OFF)
# Options for location of OpenCV Java.
set(OPENCV_JAVA_INSTALL_DIR "" CACHE PATH "Location to search for the OpenCV jar file")
# Options for compilation flags.
option(NO_WERROR "Disable -Werror flag during compilation" OFF)
# Set default build type to release with debug info (i.e. release mode optimizations
# are performed, but debug info still exists).
if(NOT CMAKE_BUILD_TYPE)
@@ -211,7 +214,6 @@ endif()
find_package(LIBSSH 0.7.1)
find_package(Protobuf REQUIRED)
find_program(Quickbuf_EXECUTABLE NAMES protoc-gen-quickbuf DOC "The Quickbuf protoc plugin")
set(APRILTAG_DEP_REPLACE "find_dependency(apriltag)")
set(CAMERASERVER_DEP_REPLACE_IMPL "find_dependency(cameraserver)")

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

@@ -1,5 +1,5 @@
## Publishing Third Party Dependencies
Currently the 3rd party deps are imgui, opencv, and google test
Currently the 3rd party deps are imgui, opencv, google test, libssh, and apriltaglib
For publishing these dependencies, the version needs to be manually updated in the publish.gradle file of their respective repository.
Then, in the azure build for the dependency you want to build for, manually start a pipeline build (As of current, this is the `Run Pipeline` button).
@@ -24,4 +24,4 @@ Upon pushing a tag, a release will be built, and the files will be uploaded to t
Before publishing, make sure to update the version in build.gradle. Publishing must happen locally, using the command `./gradlew publishPlugin`. This does require your API key for publishing to be set.
## Building the installer
Update the GradleRIO version in gradle.properties, and in the scripts folder in vscode, update the vscode extension. Then push, it will build the installer on azure.
Update the GradleRIO version in gradle.properties, and in the scripts folder in vscode, update the vscode extension. To publish a release build, upload a new tag, and a release will automatically be built and published to artifactory and cloudflare.

View File

@@ -145,6 +145,11 @@ All artifacts are based at `edu.wpi.first.artifactname` in the repository.
* wpinet
* wpiutil
* wpiunits
* apriltag
* wpiutil
* wpimath
### Third Party Artifacts
@@ -152,6 +157,7 @@ This repository provides the builds of the following third party software.
All artifacts are based at `edu.wpi.first.thirdparty.frcYEAR` in the repository.
* apriltaglib
* googletest
* imgui
* opencv

View File

@@ -63,6 +63,8 @@ The following build options are available:
* TODO
* `OPENCV_JAVA_INSTALL_DIR`
* Set this option to the location of the archive of the OpenCV Java bindings (it should be called opencv-xxx.jar, with the x'es being version numbers). NOTE: set it to the LOCATION of the file, not the file itself!
* `NO_WERROR` (OFF Default)
* This option will disable the `-Werror` compilation flag for non-MSVC builds.
## Build Setup

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

28
apriltag/README.md Normal file
View File

@@ -0,0 +1,28 @@
# AprilTag
## Adding new field to AprilTagFields
### Adding field JSON
1. Add a field layout CSV file to `src/main/native/resources/edu/wpi/first/apriltag`
1. See docstring in `convert_apriltag_layouts.py` for more
2. Run `convert_apriltag_layouts.py` in the same directory as this readme to generate the JSON
3. That script overwrites all generated JSONs, so undo undesired changes if necessary
4. Update the field dimensions at the bottom of the JSON
1. Length should be in meters from alliance wall to alliance wall
2. Width should be in meters from inside guardrail plastic to plastic
### Java updates
1. Update `src/main/java/edu/wpi/first/apriltag/AprilTagFields.java`
1. Add enum value for new field to `AprilTagFields`
2. Update `AprilTagFields.kDefaultField` if necessary
### C++ updates
1. Update `src/main/native/include/frc/apriltag/AprilTagFields.h`
1. Add enum value for new field to `AprilTagFields`
2. Update `AprilTagFields::kDefaultField` if necessary
2. Update `src/main/native/cpp/AprilTagFields.cpp`
1. Add resource getter prototype like `std::string_view GetResource_2024_crescendo_json()`
2. Add case for new field to switch in `LoadAprilTagLayoutField()`

View File

@@ -0,0 +1,89 @@
#!/usr/bin/env python3
"""
This script converts all AprilTag field layout CSV files in
src/main/native/resources/edu/wpi/first/apriltag to the JSON format
AprilTagFields expects.
The input CSV has the following format:
* Columns: ID, X, Y, Z, Rotation
* ID is a positive integer
* X, Y, and Z are decimal inches
* Rotation is yaw in degrees
The values come from a table in the layout marking diagram (e.g.,
https://firstfrc.blob.core.windows.net/frc2024/FieldAssets/2024LayoutMarkingDiagram.pdf).
"""
import csv
import json
import os
from wpimath import geometry, units
import numpy as np
def main():
# Find AprilTag field layout CSVs
filenames = [
os.path.join(dp, f)
for dp, dn, fn in os.walk("src/main/native/resources/edu/wpi/first/apriltag")
for f in fn
if f.endswith(".csv")
]
for filename in filenames:
json_data = {"tags": [], "field": {"length": 0.0, "width": 0.0}}
# Read CSV and fill in JSON data
with open(filename, newline="") as csvfile:
reader = csv.reader(csvfile, delimiter=",")
# Skip header
next(reader)
for row in reader:
# Unpack row elements
id = int(row[0])
x = float(row[1])
y = float(row[2])
z = float(row[3])
rotation = float(row[4])
# Turn yaw into quaternion
q = geometry.Rotation3d(
units.radians(0.0),
units.radians(0.0),
units.degreesToRadians(rotation),
).getQuaternion()
json_data["tags"].append(
{
"ID": id,
"pose": {
"translation": {
"x": units.inchesToMeters(x),
"y": units.inchesToMeters(y),
"z": units.inchesToMeters(z),
},
"rotation": {
"quaternion": {
"W": q.W(),
"X": q.X(),
"Y": q.Y(),
"Z": q.Z(),
}
},
},
}
)
# Write JSON
with open(filename.replace(".csv", ".json"), "w") as f:
json.dump(json_data, f, indent=2)
f.write("\n")
if __name__ == "__main__":
main()

View File

@@ -11,14 +11,23 @@ import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.util.RawFrame;
import java.util.Objects;
/** Represents an AprilTag's metadata. */
@SuppressWarnings("MemberName")
public class AprilTag {
/** The tag's ID. */
@JsonProperty(value = "ID")
public int ID;
/** The tag's pose. */
@JsonProperty(value = "pose")
public Pose3d pose;
/**
* Constructs an AprilTag.
*
* @param ID The tag's ID.
* @param pose The tag's pose.
*/
@SuppressWarnings("ParameterName")
@JsonCreator
public AprilTag(
@@ -54,9 +63,9 @@ public class AprilTag {
* @return A RawFrame containing the AprilTag image
*/
public static RawFrame generate16h5AprilTagImage(int id) {
RawFrame generatedImage = new RawFrame();
AprilTagJNI.generate16h5AprilTagImage(id, generatedImage.getDataPtr());
return generatedImage;
RawFrame frame = new RawFrame();
AprilTagJNI.generate16h5AprilTagImage(frame, frame.getNativeObj(), id);
return frame;
}
/**
@@ -66,8 +75,8 @@ public class AprilTag {
* @return A RawFrame containing the AprilTag image
*/
public static RawFrame generate36h11AprilTagImage(int id) {
RawFrame generatedImage = new RawFrame();
AprilTagJNI.generate36h11AprilTagImage(id, generatedImage.getDataPtr());
return generatedImage;
RawFrame frame = new RawFrame();
AprilTagJNI.generate36h11AprilTagImage(frame, frame.getNativeObj(), id);
return frame;
}
}

View File

@@ -57,8 +57,21 @@ public class AprilTagDetector implements AutoCloseable {
*/
public boolean debug;
/** Default constructor. */
public Config() {}
/**
* Constructs a detector configuration.
*
* @param numThreads How many threads should be used for computation.
* @param quadDecimate Quad decimation.
* @param quadSigma What Gaussian blur should be applied to the segmented image (used for quad
* detection).
* @param refineEdges When true, the edges of the each quad are adjusted to "snap to" strong
* gradients nearby.
* @param decodeSharpening How much sharpening should be done to decoded images.
* @param debug Debug mode.
*/
Config(
int numThreads,
float quadDecimate,
@@ -139,8 +152,21 @@ public class AprilTagDetector implements AutoCloseable {
*/
public boolean deglitch;
/** Default constructor. */
public QuadThresholdParameters() {}
/**
* Constructs quad threshold parameters.
*
* @param minClusterPixels Threshold used to reject quads containing too few pixels.
* @param maxNumMaxima How many corner candidates to consider when segmenting a group of pixels
* into a quad.
* @param criticalAngle Critical angle, in radians.
* @param maxLineFitMSE When fitting lines to the contours, the maximum mean squared error
* allowed.
* @param minWhiteBlackDiff Minimum brightness offset.
* @param deglitch Whether the thresholded image be should be deglitched.
*/
QuadThresholdParameters(
int minClusterPixels,
int maxNumMaxima,
@@ -182,6 +208,7 @@ public class AprilTagDetector implements AutoCloseable {
}
}
/** Constructs an AprilTagDetector. */
public AprilTagDetector() {
m_native = AprilTagJNI.createDetector();
}

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;
@@ -44,8 +45,11 @@ import java.util.Optional;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE)
public class AprilTagFieldLayout {
/** Common origin positions for the AprilTag coordinate system. */
public enum OriginPosition {
/** Blue alliance wall, right side. */
kBlueAllianceWallRightSide,
/** Red alliance wall, right side. */
kRedAllianceWallRightSide,
}
@@ -218,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,18 +4,24 @@
package edu.wpi.first.apriltag;
import java.io.IOException;
import java.io.UncheckedIOException;
/** Loadable AprilTag field layouts. */
public enum AprilTagFields {
/** 2022 Rapid React. */
k2022RapidReact("2022-rapidreact.json"),
k2023ChargedUp("2023-chargedup.json");
/** 2023 Charged Up. */
k2023ChargedUp("2023-chargedup.json"),
/** 2024 Crescendo. */
k2024Crescendo("2024-crescendo.json");
/** Base resource directory. */
public static final String kBaseResourceDir = "/edu/wpi/first/apriltag/";
/** Alias to the current game. */
public static final AprilTagFields kDefaultField = k2023ChargedUp;
public static final AprilTagFields kDefaultField = k2024Crescendo;
/** Resource filename. */
public final String m_resourceFile;
AprilTagFields(String resourceFile) {
@@ -29,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

@@ -29,10 +29,19 @@ public class AprilTagPoseEstimator {
this.cy = cy;
}
/** Tag size, in meters. */
public double tagSize;
/** Camera horizontal focal length, in pixels. */
public double fx;
/** Camera vertical focal length, in pixels. */
public double fy;
/** Camera horizontal focal center, in pixels. */
public double cx;
/** Camera vertical focal center, in pixels. */
public double cy;
@Override

View File

@@ -8,25 +8,41 @@ import edu.wpi.first.apriltag.AprilTagDetection;
import edu.wpi.first.apriltag.AprilTagDetector;
import edu.wpi.first.apriltag.AprilTagPoseEstimate;
import edu.wpi.first.math.geometry.Transform3d;
import edu.wpi.first.util.RawFrame;
import edu.wpi.first.util.RuntimeLoader;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
/** AprilTag JNI. */
public class AprilTagJNI {
static boolean libraryLoaded = false;
static RuntimeLoader<AprilTagJNI> loader = null;
/** Sets whether JNI should be loaded in the static block. */
public static class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
/**
* Returns true if the JNI should be loaded in the static block.
*
* @return True if the JNI should be loaded in the static block.
*/
public static boolean getExtractOnStaticLoad() {
return extractOnStaticLoad.get();
}
/**
* Sets whether the JNI should be loaded in the static block.
*
* @param load Whether the JNI should be loaded in the static block.
*/
public static void setExtractOnStaticLoad(boolean load) {
extractOnStaticLoad.set(load);
}
/** Utility class. */
private Helper() {}
}
static {
@@ -190,7 +206,24 @@ public class AprilTagJNI {
double cx,
double cy);
public static native void generate16h5AprilTagImage(int id, long nativeAddr);
/**
* Generates a RawFrame containing the apriltag with the id with family 16h5 passed in.
*
* @param frameObj generated frame (output parameter).
* @param frame raw frame handle
* @param id id
*/
public static native void generate16h5AprilTagImage(RawFrame frameObj, long frame, int id);
public static native void generate36h11AprilTagImage(int id, long nativeAddr);
/**
* Generates a RawFrame containing the apriltag with the id with family 36h11 passed in.
*
* @param frameObj generated frame (output parameter).
* @param frame raw frame handle
* @param id id
*/
public static native void generate36h11AprilTagImage(RawFrame frameObj, long frame, int id);
/** Utility class. */
private AprilTagJNI() {}
}

View File

@@ -4,6 +4,8 @@
#include "frc/apriltag/AprilTag.h"
#include <cstring>
#include <wpi/json.h>
#ifdef _WIN32
@@ -20,40 +22,33 @@
using namespace frc;
wpi::RawFrame AprilTag::Generate36h11AprilTagImage(int id) {
apriltag_family_t* tagFamily = tag36h11_create();
image_u8_t* image = apriltag_to_image(tagFamily, id);
wpi::RawFrame markerFrame{};
size_t totalDataSize = image->height * image->stride * sizeof(char);
markerFrame.data = static_cast<char*>(
std::calloc(image->height * image->stride, sizeof(char)));
std::memcpy(markerFrame.data, image->buf, totalDataSize);
markerFrame.dataLength = image->width;
markerFrame.height = image->height;
markerFrame.pixelFormat = WPI_PIXFMT_GRAY;
markerFrame.width = image->stride;
markerFrame.totalData = totalDataSize;
static bool FamilyToImage(wpi::RawFrame* frame, apriltag_family_t* family,
int id) {
image_u8_t* image = apriltag_to_image(family, id);
size_t totalDataSize = image->height * image->stride;
bool rv = frame->Reserve(totalDataSize);
std::memcpy(frame->data, image->buf, totalDataSize);
frame->size = totalDataSize;
frame->width = image->width;
frame->height = image->height;
frame->stride = image->stride;
frame->pixelFormat = WPI_PIXFMT_GRAY;
image_u8_destroy(image);
tag36h11_destroy(tagFamily);
return markerFrame;
return rv;
}
wpi::RawFrame AprilTag::Generate16h5AprilTagImage(int id) {
bool AprilTag::Generate36h11AprilTagImage(wpi::RawFrame* frame, int id) {
apriltag_family_t* tagFamily = tag36h11_create();
bool rv = FamilyToImage(frame, tagFamily, id);
tag36h11_destroy(tagFamily);
return rv;
}
bool AprilTag::Generate16h5AprilTagImage(wpi::RawFrame* frame, int id) {
apriltag_family_t* tagFamily = tag16h5_create();
image_u8_t* image = apriltag_to_image(tagFamily, id);
wpi::RawFrame markerFrame{};
size_t totalDataSize = image->height * image->stride * sizeof(char);
markerFrame.data = static_cast<char*>(
std::calloc(image->height * image->stride, sizeof(char)));
std::memcpy(markerFrame.data, image->buf, totalDataSize);
markerFrame.dataLength = image->width;
markerFrame.height = image->height;
markerFrame.pixelFormat = WPI_PIXFMT_GRAY;
markerFrame.width = image->stride;
markerFrame.totalData = totalDataSize;
image_u8_destroy(image);
bool rv = FamilyToImage(frame, tagFamily, id);
tag16h5_destroy(tagFamily);
return markerFrame;
return rv;
}
void frc::to_json(wpi::json& json, const AprilTag& apriltag) {

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,32 +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();
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::kNumFields:
throw std::invalid_argument("Invalid Field");
}
wpi::json json = wpi::json::parse(fieldString);
return json.get<AprilTagFieldLayout>();
}
} // namespace frc

View File

@@ -2,9 +2,13 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <jni.h>
#include <cstdio>
#include <cstring>
#define WPI_RAWFRAME_JNI
#include <wpi/RawFrame.h>
#include <wpi/jni_util.h>
#include "edu_wpi_first_apriltag_jni_AprilTagJNI.h"
@@ -25,6 +29,7 @@ static JClass quaternionCls;
static JClass rotation3dCls;
static JClass transform3dCls;
static JClass translation3dCls;
static JClass rawFrameCls;
static JException illegalArgEx;
static JException nullPointerEx;
@@ -37,7 +42,8 @@ static const JClassInit classes[] = {
{"edu/wpi/first/math/geometry/Quaternion", &quaternionCls},
{"edu/wpi/first/math/geometry/Rotation3d", &rotation3dCls},
{"edu/wpi/first/math/geometry/Transform3d", &transform3dCls},
{"edu/wpi/first/math/geometry/Translation3d", &translation3dCls}};
{"edu/wpi/first/math/geometry/Translation3d", &translation3dCls},
{"edu/wpi/first/util/RawFrame", &rawFrameCls}};
static const JExceptionInit exceptions[] = {
{"java/lang/IllegalArgumentException", &illegalArgEx},
@@ -591,27 +597,38 @@ Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePose
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: generate16h5AprilTagImage
* Signature: (IJ)V
* Signature: (Ljava/lang/Object;JI)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_generate16h5AprilTagImage
(JNIEnv* env, jclass, jint id, jlong framePtr)
(JNIEnv* env, jclass, jobject frameObj, jlong framePtr, jint id)
{
wpi::RawFrame* javaRawFrame = (wpi::RawFrame*)framePtr;
*javaRawFrame = AprilTag::Generate16h5AprilTagImage(id);
auto* frame = reinterpret_cast<wpi::RawFrame*>(framePtr);
if (!frame) {
nullPointerEx.Throw(env, "frame is null");
return;
}
bool newData = AprilTag::Generate16h5AprilTagImage(frame, id);
wpi::SetFrameData(env, rawFrameCls, frameObj, *frame, newData);
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: generate36h11AprilTagImage
* Signature: (IJ)V
* Signature: (Ljava/lang/Object;JI)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_generate36h11AprilTagImage
(JNIEnv* env, jclass, jint id, jlong framePtr)
(JNIEnv* env, jclass, jobject frameObj, jlong framePtr, jint id)
{
wpi::RawFrame* javaRawFrame = (wpi::RawFrame*)framePtr;
*javaRawFrame = AprilTag::Generate36h11AprilTagImage(id);
auto* frame = reinterpret_cast<wpi::RawFrame*>(framePtr);
if (!frame) {
nullPointerEx.Throw(env, "frame is null");
return;
}
// function might reallocate
bool newData = AprilTag::Generate36h11AprilTagImage(frame, id);
wpi::SetFrameData(env, rawFrameCls, frameObj, *frame, newData);
}
} // extern "C"

View File

@@ -12,18 +12,20 @@
namespace frc {
/**
* Represents an AprilTag's metadata.
*/
struct WPILIB_DLLEXPORT AprilTag {
/// The tag's ID.
int ID;
/// The tag's pose.
Pose3d pose;
/**
* Checks equality between this AprilTag and another object.
*/
bool operator==(const AprilTag&) const = default;
static wpi::RawFrame Generate36h11AprilTagImage(int id);
static wpi::RawFrame Generate16h5AprilTagImage(int id);
static bool Generate36h11AprilTagImage(wpi::RawFrame* frame, int id);
static bool Generate16h5AprilTagImage(wpi::RawFrame* frame, int id);
};
WPILIB_DLLEXPORT

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 {
@@ -38,11 +39,24 @@ namespace frc {
* towards the opposing alliance). */
class WPILIB_DLLEXPORT AprilTagFieldLayout {
public:
/**
* Common origin positions for the AprilTag coordinate system.
*/
enum class OriginPosition {
/// Blue alliance wall, right side.
kBlueAllianceWallRightSide,
/// Red alliance wall, right side.
kRedAllianceWallRightSide,
};
/**
* Loads an AprilTagFieldLayout from a predefined field
*
* @param field The predefined field
* @return AprilTagFieldLayout of the field
*/
static AprilTagFieldLayout LoadField(AprilTagField field);
AprilTagFieldLayout() = default;
/**
@@ -147,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,25 +8,22 @@
#include <wpi/SymbolExports.h>
#include "frc/apriltag/AprilTagFieldLayout.h"
namespace frc {
/**
* Loadable AprilTag field layouts.
*/
enum class AprilTagField {
/// 2022 Rapid React.
k2022RapidReact,
/// 2023 Charged Up.
k2023ChargedUp,
/// 2024 Crescendo.
k2024Crescendo,
// This is a placeholder for denoting the last supported field. This should
// always be the last entry in the enum and should not be used by users
kNumFields,
};
/**
* Loads an AprilTagFieldLayout from a predefined field
*
* @param field The predefined field
*/
WPILIB_DLLEXPORT AprilTagFieldLayout
LoadAprilTagLayoutField(AprilTagField field);
} // namespace frc

View File

@@ -0,0 +1,17 @@
ID,X,Y,Z,Rotation
1,593.68,9.68,53.38,120
2,637.21,34.79,53.38,120
3,652.73,196.17,57.13,180
4,652.73,218.42,57.13,180
5,578.77,323.00,53.38,270
6,72.5,323.00,53.38,270
7,-1.50,218.42,57.13,0
8,-1.50,196.17,57.13,0
9,14.02,34.79,53.38,60
10,57.54,9.68,53.38,60
11,468.69,146.19,52.00,300
12,468.69,177.10,52.00,60
13,441.74,161.62,52.00,180
14,209.48,161.62,52.00,0
15,182.73,177.10,52.00,120
16,182.73,146.19,52.00,240
1 ID X Y Z Rotation
2 1 593.68 9.68 53.38 120
3 2 637.21 34.79 53.38 120
4 3 652.73 196.17 57.13 180
5 4 652.73 218.42 57.13 180
6 5 578.77 323.00 53.38 270
7 6 72.5 323.00 53.38 270
8 7 -1.50 218.42 57.13 0
9 8 -1.50 196.17 57.13 0
10 9 14.02 34.79 53.38 60
11 10 57.54 9.68 53.38 60
12 11 468.69 146.19 52.00 300
13 12 468.69 177.10 52.00 60
14 13 441.74 161.62 52.00 180
15 14 209.48 161.62 52.00 0
16 15 182.73 177.10 52.00 120
17 16 182.73 146.19 52.00 240

View File

@@ -0,0 +1,296 @@
{
"tags": [
{
"ID": 1,
"pose": {
"translation": {
"x": 15.079471999999997,
"y": 0.24587199999999998,
"z": 1.355852
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 2,
"pose": {
"translation": {
"x": 16.185134,
"y": 0.883666,
"z": 1.355852
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 3,
"pose": {
"translation": {
"x": 16.579342,
"y": 4.982717999999999,
"z": 1.4511020000000001
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 4,
"pose": {
"translation": {
"x": 16.579342,
"y": 5.547867999999999,
"z": 1.4511020000000001
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 5,
"pose": {
"translation": {
"x": 14.700757999999999,
"y": 8.2042,
"z": 1.355852
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 6,
"pose": {
"translation": {
"x": 1.8415,
"y": 8.2042,
"z": 1.355852
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 7,
"pose": {
"translation": {
"x": -0.038099999999999995,
"y": 5.547867999999999,
"z": 1.4511020000000001
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 8,
"pose": {
"translation": {
"x": -0.038099999999999995,
"y": 4.982717999999999,
"z": 1.4511020000000001
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 9,
"pose": {
"translation": {
"x": 0.356108,
"y": 0.883666,
"z": 1.355852
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 10,
"pose": {
"translation": {
"x": 1.4615159999999998,
"y": 0.24587199999999998,
"z": 1.355852
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 11,
"pose": {
"translation": {
"x": 11.904726,
"y": 3.7132259999999997,
"z": 1.3208
},
"rotation": {
"quaternion": {
"W": -0.8660254037844387,
"X": -0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 12,
"pose": {
"translation": {
"x": 11.904726,
"y": 4.49834,
"z": 1.3208
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 13,
"pose": {
"translation": {
"x": 11.220196,
"y": 4.105148,
"z": 1.3208
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 14,
"pose": {
"translation": {
"x": 5.320792,
"y": 4.105148,
"z": 1.3208
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 15,
"pose": {
"translation": {
"x": 4.641342,
"y": 4.49834,
"z": 1.3208
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 16,
"pose": {
"translation": {
"x": 4.641342,
"y": 3.7132259999999997,
"z": 1.3208
},
"rotation": {
"quaternion": {
"W": -0.4999999999999998,
"X": -0.0,
"Y": 0.0,
"Z": 0.8660254037844387
}
}
}
}
],
"field": {
"length": 16.541,
"width": 8.211
}
}

View File

@@ -0,0 +1,34 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.apriltag;
import static org.junit.jupiter.api.Assertions.assertEquals;
import edu.wpi.first.util.PixelFormat;
import org.junit.jupiter.api.Test;
class AprilTagGenerationTest {
@Test
void test36h11() {
var frame = AprilTag.generate36h11AprilTagImage(1);
assertEquals(PixelFormat.kGray, frame.getPixelFormat());
assertEquals(10, frame.getWidth());
assertEquals(10, frame.getHeight());
int stride = frame.getStride();
assertEquals(stride * 10, frame.getSize());
// check the diagonal values
var data = frame.getData();
assertEquals(-1, data.get(stride * 0 + 0)); // outer border is white
assertEquals(0, data.get(stride * 1 + 1)); // inner border is black
assertEquals(-1, data.get(stride * 2 + 2));
assertEquals(-1, data.get(stride * 3 + 3));
assertEquals(-1, data.get(stride * 4 + 4));
assertEquals(0, data.get(stride * 5 + 5));
assertEquals(0, data.get(stride * 6 + 6));
assertEquals(-1, data.get(stride * 7 + 7));
assertEquals(0, data.get(stride * 8 + 8)); // inner border
assertEquals(-1, data.get(stride * 9 + 9)); // outer border
}
}

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

@@ -14,7 +14,6 @@ import edu.wpi.first.cscore.VideoEvent;
import edu.wpi.first.cscore.VideoException;
import edu.wpi.first.cscore.VideoListener;
import edu.wpi.first.cscore.VideoMode;
import edu.wpi.first.cscore.VideoMode.PixelFormat;
import edu.wpi.first.cscore.VideoSink;
import edu.wpi.first.cscore.VideoSource;
import edu.wpi.first.networktables.BooleanEntry;
@@ -27,6 +26,8 @@ import edu.wpi.first.networktables.StringArrayPublisher;
import edu.wpi.first.networktables.StringArrayTopic;
import edu.wpi.first.networktables.StringEntry;
import edu.wpi.first.networktables.StringPublisher;
import edu.wpi.first.util.PixelFormat;
import java.lang.ref.Reference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -39,6 +40,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* NetworkTables.
*/
public final class CameraServer {
/** CameraServer base port. */
public static final int kBasePort = 1181;
private static final String kPublishName = "/CameraPublisher";
@@ -139,6 +141,7 @@ public final class CameraServer {
if (m_choicesPublisher != null) {
m_choicesPublisher.close();
}
Reference.reachabilityFence(m_videoListener);
}
BooleanEntry m_booleanValueEntry;
@@ -222,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 -> {
@@ -686,7 +689,7 @@ public final class CameraServer {
*/
public static MjpegServer addSwitchedCamera(String name) {
// create a dummy CvSource
CvSource source = new CvSource(name, VideoMode.PixelFormat.kMJPEG, 160, 120, 30);
CvSource source = new CvSource(name, PixelFormat.kMJPEG, 160, 120, 30);
MjpegServer server = startAutomaticCapture(source);
synchronized (CameraServer.class) {
m_fixedSources.put(server.getHandle(), source.getHandle());
@@ -801,7 +804,7 @@ public final class CameraServer {
* @return OpenCV source for the MJPEG stream
*/
public static CvSource putVideo(String name, int width, int height) {
CvSource source = new CvSource(name, VideoMode.PixelFormat.kMJPEG, width, height, 30);
CvSource source = new CvSource(name, PixelFormat.kMJPEG, width, height, 30);
startAutomaticCapture(source);
return source;
}

View File

@@ -4,6 +4,7 @@
package edu.wpi.first.cameraserver;
/** CameraServer shared functions. */
public interface CameraServerShared {
/**
* get the main thread id func.

View File

@@ -4,6 +4,7 @@
package edu.wpi.first.cameraserver;
/** Storage for CameraServerShared instance. */
public final class CameraServerSharedStore {
private static CameraServerShared cameraServerShared;

View File

@@ -14,6 +14,7 @@ import org.opencv.core.Mat;
* code. The easiest way to use this is to run it in a {@link VisionThread} and use the listener to
* take snapshots of the pipeline's outputs.
*
* @param <P> Vision pipeline type.
* @see VisionPipeline
* @see VisionThread
* @see <a href="package-summary.html">vision</a>

View File

@@ -22,10 +22,8 @@ namespace frc {
*/
class CameraServer {
public:
/// CameraServer base port.
static constexpr uint16_t kBasePort = 1181;
static constexpr int kSize640x480 = 0;
static constexpr int kSize320x240 = 1;
static constexpr int kSize160x120 = 2;
/**
* Start automatically capturing images to send to the dashboard.

View File

@@ -1,9 +1,17 @@
macro(wpilib_target_warnings target)
if(NOT MSVC)
target_compile_options(
${target}
PRIVATE -Wall -pedantic -Wextra -Werror -Wno-unused-parameter ${WPILIB_TARGET_WARNINGS}
set(WARNING_FLAGS
-Wall
-pedantic
-Wextra
-Wno-unused-parameter
${WPILIB_TARGET_WARNINGS}
)
if(NOT NO_WERROR)
set(WARNING_FLAGS ${WARNING_FLAGS} -Werror)
endif()
target_compile_options(${target} PRIVATE ${WARNING_FLAGS})
else()
target_compile_options(
${target}

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

@@ -9,21 +9,36 @@ import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.opencv.core.Core;
/** CameraServer CV JNI. */
public class CameraServerCvJNI {
static boolean libraryLoaded = false;
static RuntimeLoader<Core> loader = null;
/** Sets whether JNI should be loaded in the static block. */
public static class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
/**
* Returns true if the JNI should be loaded in the static block.
*
* @return True if the JNI should be loaded in the static block.
*/
public static boolean getExtractOnStaticLoad() {
return extractOnStaticLoad.get();
}
/**
* Sets whether the JNI should be loaded in the static block.
*
* @param load Whether the JNI should be loaded in the static block.
*/
public static void setExtractOnStaticLoad(boolean load) {
extractOnStaticLoad.set(load);
}
/** Utility class. */
private Helper() {}
}
static {
@@ -59,17 +74,64 @@ 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. */
private CameraServerCvJNI() {}
}

View File

@@ -4,7 +4,7 @@
package edu.wpi.first.cscore;
import edu.wpi.first.cscore.VideoMode.PixelFormat;
import edu.wpi.first.util.PixelFormat;
import org.opencv.core.Mat;
/**

View File

@@ -4,6 +4,7 @@
package edu.wpi.first.cscore;
import edu.wpi.first.util.PixelFormat;
import org.opencv.core.Mat;
/**
@@ -32,7 +33,7 @@ public class CvSource extends ImageSource {
* @param height height
* @param fps fps
*/
public CvSource(String name, VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
public CvSource(String name, PixelFormat pixelFormat, int width, int height, int fps) {
super(CameraServerCvJNI.createCvSource(name, pixelFormat.getValue(), width, height, fps));
}

View File

@@ -6,10 +6,15 @@ 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),
/** MJPG Streamer camera. */
kMJPGStreamer(1),
/** CS Core camera. */
kCSCore(2),
/** Axis camera. */
kAxis(3);
private final int value;
@@ -18,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,14 +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,27 +7,49 @@ package edu.wpi.first.cscore;
/** Video event. */
@SuppressWarnings("MemberName")
public class VideoEvent {
/** VideoEvent kind. */
public enum Kind {
/** Unknown video event. */
kUnknown(0x0000),
/** Source Created event. */
kSourceCreated(0x0001),
/** Source Destroyed event. */
kSourceDestroyed(0x0002),
/** Source Connected event. */
kSourceConnected(0x0004),
/** Source Disconnected event. */
kSourceDisconnected(0x0008),
/** Source Video Modes Updated event. */
kSourceVideoModesUpdated(0x0010),
/** Source VideoMode Changed event. */
kSourceVideoModeChanged(0x0020),
/** Source Property Created event. */
kSourcePropertyCreated(0x0040),
/** Source Property Value Updated event. */
kSourcePropertyValueUpdated(0x0080),
/** Source Property Choices Updated event. */
kSourcePropertyChoicesUpdated(0x0100),
/** Sink Source Changed event. */
kSinkSourceChanged(0x0200),
/** Sink Created event. */
kSinkCreated(0x0400),
/** Sink Destroyed event. */
kSinkDestroyed(0x0800),
/** Sink Enabled event. */
kSinkEnabled(0x1000),
/** Sink Disabled event. */
kSinkDisabled(0x2000),
/** Network Interfaces Changed event. */
kNetworkInterfacesChanged(0x4000),
/** Telemetry Updated event. */
kTelemetryUpdated(0x8000),
/** Sink Property Created event. */
kSinkPropertyCreated(0x10000),
/** Sink Property Value Updated event. */
kSinkPropertyValueUpdated(0x20000),
/** Sink Property Choices Updated event. */
kSinkPropertyChoicesUpdated(0x40000),
/** Usb Cameras Changed event. */
kUsbCamerasChanged(0x80000);
private final int value;
@@ -36,6 +58,11 @@ public class VideoEvent {
this.value = value;
}
/**
* Returns the kind value.
*
* @return The kind value.
*/
public int getValue() {
return value;
}
@@ -118,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

@@ -4,38 +4,12 @@
package edu.wpi.first.cscore;
import edu.wpi.first.util.PixelFormat;
import java.util.Objects;
/** Video mode. */
@SuppressWarnings("MemberName")
public class VideoMode {
public enum PixelFormat {
kUnknown(0),
kMJPEG(1),
kYUYV(2),
kRGB565(3),
kBGR(4),
kGray(5),
kY16(6),
kUYVY(7);
private final int value;
PixelFormat(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
private static final PixelFormat[] m_pixelFormatValues = PixelFormat.values();
public static PixelFormat getPixelFormatFromInt(int pixelFormat) {
return m_pixelFormatValues[pixelFormat];
}
/**
* Create a new video mode.
*
@@ -45,7 +19,7 @@ public class VideoMode {
* @param fps The camera's frames per second.
*/
public VideoMode(int pixelFormat, int width, int height, int fps) {
this.pixelFormat = getPixelFormatFromInt(pixelFormat);
this.pixelFormat = PixelFormat.getFromInt(pixelFormat);
this.width = width;
this.height = height;
this.fps = fps;

View File

@@ -6,11 +6,17 @@ package edu.wpi.first.cscore;
/** A source or sink property. */
public class VideoProperty {
/** VideoProperty property types. */
public enum Kind {
/** No specific property. */
kNone(0),
/** Boolean property. */
kBoolean(1),
/** Integer property. */
kInteger(2),
/** String property. */
kString(4),
/** Enum property. */
kEnum(8);
private final int value;
@@ -19,6 +25,11 @@ public class VideoProperty {
this.value = value;
}
/**
* Returns the Kind value.
*
* @return The Kind value.
*/
public int getValue() {
return value;
}
@@ -45,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

@@ -9,10 +9,15 @@ package edu.wpi.first.cscore;
* (e.g. from a stereo or depth camera); these are called channels.
*/
public class VideoSink implements AutoCloseable {
/** Video sink types. */
public enum Kind {
/** Unknown video sink type. */
kUnknown(0),
/** MJPEG video sink. */
kMjpeg(2),
/** CV video sink. */
kCv(4),
/** Raw video sink. */
kRaw(8);
private final int value;
@@ -21,6 +26,11 @@ public class VideoSink implements AutoCloseable {
this.value = value;
}
/**
* Returns the Kind value.
*
* @return The Kind value.
*/
public int getValue() {
return value;
}
@@ -43,6 +53,11 @@ public class VideoSink implements AutoCloseable {
}
}
/**
* Constructs a VideoSink.
*
* @param handle The video sink handle.
*/
protected VideoSink(int handle) {
m_handle = handle;
}
@@ -55,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;
}
@@ -217,5 +242,6 @@ public class VideoSink implements AutoCloseable {
return rv;
}
/** The VideoSink handle. */
protected int m_handle;
}

View File

@@ -4,16 +4,24 @@
package edu.wpi.first.cscore;
import edu.wpi.first.util.PixelFormat;
/**
* A source for video that provides a sequence of frames. Each frame may consist of multiple images
* (e.g. from a stereo or depth camera); these are called channels.
*/
public class VideoSource implements AutoCloseable {
/** Video source kind. */
public enum Kind {
/** Unknown video source. */
kUnknown(0),
/** USB video source. */
kUsb(1),
/** HTTP video source. */
kHttp(2),
/** CV video source. */
kCv(4),
/** Raw video source. */
kRaw(8);
private final int value;
@@ -22,6 +30,11 @@ public class VideoSource implements AutoCloseable {
this.value = value;
}
/**
* Returns the Kind value.
*
* @return The Kind value.
*/
public int getValue() {
return value;
}
@@ -49,6 +62,11 @@ public class VideoSource implements AutoCloseable {
this.value = value;
}
/**
* Returns the ConnectionStrategy value.
*
* @return The ConnectionStrategy value.
*/
public int getValue() {
return value;
}
@@ -73,6 +91,11 @@ public class VideoSource implements AutoCloseable {
}
}
/**
* Constructs a VideoSource.
*
* @param handle The video source handle.
*/
protected VideoSource(int handle) {
m_handle = handle;
}
@@ -85,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;
}
@@ -235,7 +268,7 @@ public class VideoSource implements AutoCloseable {
* @param fps desired FPS
* @return True if set successfully
*/
public boolean setVideoMode(VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
public boolean setVideoMode(PixelFormat pixelFormat, int width, int height, int fps) {
return CameraServerJNI.setSourceVideoMode(m_handle, pixelFormat.getValue(), width, height, fps);
}
@@ -245,7 +278,7 @@ public class VideoSource implements AutoCloseable {
* @param pixelFormat desired pixel format
* @return True if set successfully
*/
public boolean setPixelFormat(VideoMode.PixelFormat pixelFormat) {
public boolean setPixelFormat(PixelFormat pixelFormat) {
return CameraServerJNI.setSourcePixelFormat(m_handle, pixelFormat.getValue());
}
@@ -372,5 +405,6 @@ public class VideoSource implements AutoCloseable {
return rv;
}
/** Video source handle. */
protected int m_handle;
}

View File

@@ -33,7 +33,7 @@ public class RawSink extends ImageSink {
* @return Frame time, or 0 on error (call getError() to obtain the error message); the frame time
* is in the same time base as wpi::Now(), and is in 1 us increments.
*/
protected long grabFrame(RawFrame frame) {
public long grabFrame(RawFrame frame) {
return grabFrame(frame, 0.225);
}
@@ -46,8 +46,8 @@ public class RawSink extends ImageSink {
* @return Frame time, or 0 on error (call getError() to obtain the error message); the frame time
* is in the same time base as wpi::Now(), and is in 1 us increments.
*/
protected long grabFrame(RawFrame frame, double timeout) {
return CameraServerJNI.grabSinkFrameTimeout(m_handle, frame, timeout);
public long grabFrame(RawFrame frame, double timeout) {
return CameraServerJNI.grabRawSinkFrameTimeout(m_handle, frame, frame.getNativeObj(), timeout);
}
/**
@@ -58,7 +58,7 @@ public class RawSink extends ImageSink {
* @return Frame time, or 0 on error (call getError() to obtain the error message); the frame time
* is in the same time base as wpi::Now(), and is in 1 us increments.
*/
protected long grabFrameNoTimeout(RawFrame frame) {
return CameraServerJNI.grabSinkFrame(m_handle, frame);
public long grabFrameNoTimeout(RawFrame frame) {
return CameraServerJNI.grabRawSinkFrame(m_handle, frame, frame.getNativeObj());
}
}

View File

@@ -7,7 +7,9 @@ package edu.wpi.first.cscore.raw;
import edu.wpi.first.cscore.CameraServerJNI;
import edu.wpi.first.cscore.ImageSource;
import edu.wpi.first.cscore.VideoMode;
import edu.wpi.first.util.PixelFormat;
import edu.wpi.first.util.RawFrame;
import java.nio.ByteBuffer;
/**
* A source for user code to provide video frames as raw bytes.
@@ -36,7 +38,7 @@ public class RawSource extends ImageSource {
* @param height height
* @param fps fps
*/
public RawSource(String name, VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
public RawSource(String name, PixelFormat pixelFormat, int width, int height, int fps) {
super(CameraServerJNI.createRawSource(name, pixelFormat.getValue(), width, height, fps));
}
@@ -45,35 +47,41 @@ public class RawSource extends ImageSource {
*
* @param image raw frame image
*/
protected void putFrame(RawFrame image) {
CameraServerJNI.putRawSourceFrame(m_handle, image);
public void putFrame(RawFrame image) {
CameraServerJNI.putRawSourceFrame(m_handle, image.getNativeObj());
}
/**
* Put a raw image and notify sinks.
*
* @param data raw frame data pointer
* @param data raw frame native data pointer
* @param size total size in bytes
* @param width frame width
* @param height frame height
* @param stride size of each row in bytes
* @param pixelFormat pixel format
* @param totalData length of data in total
*/
protected void putFrame(long data, int width, int height, int pixelFormat, int totalData) {
CameraServerJNI.putRawSourceFrame(m_handle, data, width, height, pixelFormat, totalData);
}
/**
* Put a raw image and notify sinks.
*
* @param data raw frame data pointer
* @param width frame width
* @param height frame height
* @param pixelFormat pixel format
* @param totalData length of data in total
*/
protected void putFrame(
long data, int width, int height, VideoMode.PixelFormat pixelFormat, int totalData) {
CameraServerJNI.putRawSourceFrame(
m_handle, data, width, height, pixelFormat.getValue(), totalData);
long data, int size, int width, int height, int stride, PixelFormat pixelFormat) {
CameraServerJNI.putRawSourceFrameData(
m_handle, data, size, width, height, stride, pixelFormat.getValue());
}
/**
* Put a raw image and notify sinks.
*
* @param data raw frame native ByteBuffer
* @param width frame width
* @param height frame height
* @param stride size of each row in bytes
* @param pixelFormat pixel format
*/
public void putFrame(
ByteBuffer data, int width, int height, int stride, PixelFormat pixelFormat) {
if (!data.isDirect()) {
throw new UnsupportedOperationException("ByteBuffer must be direct");
}
CameraServerJNI.putRawSourceFrameBB(
m_handle, data, data.limit(), width, height, stride, pixelFormat.getValue());
}
}

View File

@@ -72,6 +72,23 @@ class Image {
return cv::Mat{height, width, type, m_data.data()};
}
int GetStride() const {
switch (pixelFormat) {
case VideoMode::kYUYV:
case VideoMode::kRGB565:
case VideoMode::kY16:
case VideoMode::kUYVY:
return 2 * width;
case VideoMode::kBGR:
return 3 * width;
case VideoMode::kGray:
return width;
case VideoMode::kMJPEG:
default:
return 0;
}
}
cv::_InputArray AsInputArray() { return cv::_InputArray{m_data}; }
bool Is(int width_, int height_) {

View File

@@ -109,10 +109,10 @@ uint64_t RawSinkImpl::GrabFrameImpl(WPI_RawFrame& rawFrame,
WPI_AllocateRawFrameData(&rawFrame, newImage->size());
rawFrame.height = newImage->height;
rawFrame.width = newImage->width;
rawFrame.stride = newImage->GetStride();
rawFrame.pixelFormat = newImage->pixelFormat;
rawFrame.totalData = newImage->size();
std::copy(newImage->data(), newImage->data() + rawFrame.totalData,
rawFrame.data);
rawFrame.size = newImage->size();
std::copy(newImage->data(), newImage->data() + rawFrame.size, rawFrame.data);
return incomingFrame.GetTime();
}

View File

@@ -39,10 +39,11 @@ void RawSourceImpl::PutFrame(const WPI_RawFrame& image) {
type = CV_8UC1;
break;
}
cv::Mat finalImage{image.height, image.width, type, image.data};
cv::Mat finalImage{image.height, image.width, type, image.data,
static_cast<size_t>(image.stride)};
std::unique_ptr<Image> dest =
AllocImage(static_cast<VideoMode::PixelFormat>(image.pixelFormat),
image.width, image.height, image.totalData);
image.width, image.height, image.size);
finalImage.copyTo(dest->AsMat());
SourceImpl::PutFrame(std::move(dest), wpi::Now());

View File

@@ -7,6 +7,9 @@
#include <fmt/format.h>
#include <opencv2/core/core.hpp>
#define WPI_RAWFRAME_JNI
#include <wpi/RawFrame.h>
#include <wpi/SmallString.h>
#include <wpi/jni_util.h>
@@ -42,7 +45,8 @@ static JNIEnv* listenerEnv = nullptr;
static const JClassInit classes[] = {
{"edu/wpi/first/cscore/UsbCameraInfo", &usbCameraInfoCls},
{"edu/wpi/first/cscore/VideoMode", &videoModeCls},
{"edu/wpi/first/cscore/VideoEvent", &videoEventCls}};
{"edu/wpi/first/cscore/VideoEvent", &videoEventCls},
{"edu/wpi/first/util/RawFrame", &rawFrameCls}};
static const JExceptionInit exceptions[] = {
{"edu/wpi/first/cscore/VideoException", &videoEx},
@@ -1219,48 +1223,78 @@ Java_edu_wpi_first_cscore_CameraServerCvJNI_putSourceFrame
}
}
// int width, int height, int pixelFormat, int totalData
/*
* Class: edu_wpi_first_cscore_CameraServerJNI
* Method: putRawSourceFrameBB
* Signature: (ILjava/lang/Object;IIII)V
* Method: putRawSourceFrame
* Signature: (IJ)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrameBB
(JNIEnv* env, jclass, jint source, jobject byteBuffer, jint width,
jint height, jint pixelFormat, jint totalData)
Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrame
(JNIEnv* env, jclass, jint source, jlong framePtr)
{
WPI_RawFrame rawFrame;
rawFrame.data =
reinterpret_cast<char*>(env->GetDirectBufferAddress(byteBuffer));
rawFrame.totalData = totalData;
rawFrame.pixelFormat = pixelFormat;
rawFrame.width = width;
rawFrame.height = height;
auto* frame = reinterpret_cast<wpi::RawFrame*>(framePtr);
if (!frame) {
nullPointerEx.Throw(env, "frame is null");
return;
}
CS_Status status = 0;
cs::PutSourceFrame(source, rawFrame, &status);
cs::PutSourceFrame(source, *frame, &status);
CheckStatus(env, status);
}
/*
* Class: edu_wpi_first_cscore_CameraServerJNI
* Method: putRawSourceFrame
* Signature: (IJIIII)V
* Method: putRawSourceFrameBB
* Signature: (ILjava/lang/Object;IIIII)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrame
(JNIEnv* env, jclass, jint source, jlong ptr, jint width, jint height,
jint pixelFormat, jint totalData)
Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrameBB
(JNIEnv* env, jclass, jint source, jobject data, jint size, jint width,
jint height, jint stride, jint pixelFormat)
{
WPI_RawFrame rawFrame;
rawFrame.data = reinterpret_cast<char*>(static_cast<intptr_t>(ptr));
rawFrame.totalData = totalData;
rawFrame.pixelFormat = pixelFormat;
rawFrame.width = width;
rawFrame.height = height;
WPI_RawFrame frame; // use WPI_Frame because we don't want the destructor
frame.data = static_cast<uint8_t*>(env->GetDirectBufferAddress(data));
if (!frame.data) {
nullPointerEx.Throw(env, "data is null");
return;
}
frame.freeFunc = nullptr;
frame.freeCbData = nullptr;
frame.size = size;
frame.width = width;
frame.height = height;
frame.stride = stride;
frame.pixelFormat = pixelFormat;
CS_Status status = 0;
cs::PutSourceFrame(source, rawFrame, &status);
cs::PutSourceFrame(source, frame, &status);
CheckStatus(env, status);
}
/*
* Class: edu_wpi_first_cscore_CameraServerJNI
* Method: putRawSourceFrameData
* Signature: (IJIIIII)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrameData
(JNIEnv* env, jclass, jint source, jlong data, jint size, jint width,
jint height, jint stride, jint pixelFormat)
{
WPI_RawFrame frame; // use WPI_Frame because we don't want the destructor
frame.data = reinterpret_cast<uint8_t*>(data);
if (!frame.data) {
nullPointerEx.Throw(env, "data is null");
return;
}
frame.freeFunc = nullptr;
frame.freeCbData = nullptr;
frame.size = size;
frame.width = width;
frame.height = height;
frame.stride = stride;
frame.pixelFormat = pixelFormat;
CS_Status status = 0;
cs::PutSourceFrame(source, frame, &status);
CheckStatus(env, status);
}
@@ -1721,72 +1755,47 @@ Java_edu_wpi_first_cscore_CameraServerCvJNI_grabSinkFrameTimeout
}
}
static void SetRawFrameData(JNIEnv* env, jobject rawFrameObj,
jobject byteBuffer, bool didChangeDataPtr,
const WPI_RawFrame& frame) {
static jmethodID setMethod =
env->GetMethodID(rawFrameCls, "setData", "(Ljava/nio/ByteBuffer;JIIII)V");
jlong framePtr = static_cast<jlong>(reinterpret_cast<intptr_t>(frame.data));
if (didChangeDataPtr) {
byteBuffer = env->NewDirectByteBuffer(frame.data, frame.dataLength);
}
env->CallVoidMethod(
rawFrameObj, setMethod, byteBuffer, framePtr,
static_cast<jint>(frame.totalData), static_cast<jint>(frame.width),
static_cast<jint>(frame.height), static_cast<jint>(frame.pixelFormat));
}
/*
* Class: edu_wpi_first_cscore_CameraServerJNI
* Method: grabRawSinkFrameImpl
* Signature: (ILjava/lang/Object;JLjava/lang/Object;III)J
* Method: grabRawSinkFrame
* Signature: (ILjava/lang/Object;J)J
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_first_cscore_CameraServerJNI_grabRawSinkFrameImpl
(JNIEnv* env, jclass, jint sink, jobject rawFrameObj, jlong rawFramePtr,
jobject byteBuffer, jint width, jint height, jint pixelFormat)
Java_edu_wpi_first_cscore_CameraServerJNI_grabRawSinkFrame
(JNIEnv* env, jclass, jint sink, jobject frameObj, jlong framePtr)
{
WPI_RawFrame* ptr =
reinterpret_cast<WPI_RawFrame*>(static_cast<intptr_t>(rawFramePtr));
auto origDataPtr = ptr->data;
ptr->width = width;
ptr->height = height;
ptr->pixelFormat = pixelFormat;
auto* frame = reinterpret_cast<wpi::RawFrame*>(framePtr);
auto origData = frame->data;
CS_Status status = 0;
auto rv = cs::GrabSinkFrame(static_cast<CS_Sink>(sink), *ptr, &status);
auto rv = cs::GrabSinkFrame(static_cast<CS_Sink>(sink), *frame, &status);
if (!CheckStatus(env, status)) {
return 0;
}
SetRawFrameData(env, rawFrameObj, byteBuffer, origDataPtr != ptr->data, *ptr);
wpi::SetFrameData(env, rawFrameCls, frameObj, *frame,
origData != frame->data);
return rv;
}
/*
* Class: edu_wpi_first_cscore_CameraServerJNI
* Method: grabRawSinkFrameTimeoutImpl
* Signature: (ILjava/lang/Object;JLjava/lang/Object;IIID)J
* Method: grabRawSinkFrameTimeout
* Signature: (ILjava/lang/Object;JD)J
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_first_cscore_CameraServerJNI_grabRawSinkFrameTimeoutImpl
(JNIEnv* env, jclass, jint sink, jobject rawFrameObj, jlong rawFramePtr,
jobject byteBuffer, jint width, jint height, jint pixelFormat,
Java_edu_wpi_first_cscore_CameraServerJNI_grabRawSinkFrameTimeout
(JNIEnv* env, jclass, jint sink, jobject frameObj, jlong framePtr,
jdouble timeout)
{
WPI_RawFrame* ptr =
reinterpret_cast<WPI_RawFrame*>(static_cast<intptr_t>(rawFramePtr));
auto origDataPtr = ptr->data;
ptr->width = width;
ptr->height = height;
ptr->pixelFormat = pixelFormat;
auto* frame = reinterpret_cast<wpi::RawFrame*>(framePtr);
auto origData = frame->data;
CS_Status status = 0;
auto rv = cs::GrabSinkFrameTimeout(static_cast<CS_Sink>(sink), *ptr, timeout,
&status);
auto rv = cs::GrabSinkFrameTimeout(static_cast<CS_Sink>(sink), *frame,
timeout, &status);
if (!CheckStatus(env, status)) {
return 0;
}
SetRawFrameData(env, rawFrameObj, byteBuffer, origDataPtr != ptr->data, *ptr);
wpi::SetFrameData(env, rawFrameCls, frameObj, *frame,
origData != frame->data);
return rv;
}

View File

@@ -44,42 +44,153 @@ class VideoProperty {
public:
enum Kind {
/// No specific property.
kNone = CS_PROP_NONE,
/// Boolean property.
kBoolean = CS_PROP_BOOLEAN,
/// Integer property.
kInteger = CS_PROP_INTEGER,
/// String property.
kString = CS_PROP_STRING,
/// Enum property.
kEnum = CS_PROP_ENUM
};
VideoProperty() = default;
/**
* Returns property name.
*
* @return Property name.
*/
std::string GetName() const;
/**
* Returns property kind.
*
* @return Property kind.
*/
Kind GetKind() const { return m_kind; }
/**
* Returns true if property is valid.
*
* @return True if property is valid.
*/
explicit operator bool() const { return m_kind != kNone; }
// Kind checkers
/**
* Returns true if property is a boolean.
*
* @return True if property is a boolean.
*/
bool IsBoolean() const { return m_kind == kBoolean; }
/**
* Returns true if property is an integer.
*
* @return True if property is an integer.
*/
bool IsInteger() const { return m_kind == kInteger; }
/**
* Returns true if property is a string.
*
* @return True if property is a string.
*/
bool IsString() const { return m_kind == kString; }
/**
* Returns true if property is an enum.
*
* @return True if property is an enum.
*/
bool IsEnum() const { return m_kind == kEnum; }
/**
* Returns property value.
*
* @return Property value.
*/
int Get() const;
/**
* Sets property value.
*
* @param value Property value.
*/
void Set(int value);
/**
* Returns property minimum value.
*
* @return Property minimum value.
*/
int GetMin() const;
/**
* Returns property maximum value.
*
* @return Property maximum value.
*/
int GetMax() const;
/**
* Returns property step size.
*
* @return Property step size.
*/
int GetStep() const;
/**
* Returns property default value.
*
* @return Property default value.
*/
int GetDefault() const;
// String-specific functions
/**
* Returns the string property value.
*
* <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:
@@ -99,10 +210,17 @@ class VideoSource {
friend class VideoSink;
public:
/**
* Video source kind.
*/
enum Kind {
/// Unknown video source.
kUnknown = CS_SOURCE_UNKNOWN,
/// USB video source.
kUsb = CS_SOURCE_USB,
/// HTTP video source.
kHttp = CS_SOURCE_HTTP,
/// CV video source.
kCv = CS_SOURCE_CV
};
@@ -350,6 +468,8 @@ class VideoSource {
explicit VideoSource(CS_Source handle) : m_handle(handle) {}
mutable CS_Status m_status = 0;
/// Video source handle.
CS_Source m_handle{0};
};
@@ -358,11 +478,19 @@ class VideoSource {
*/
class VideoCamera : public VideoSource {
public:
/**
* White balance.
*/
enum WhiteBalance {
/// Fixed indoor white balance.
kFixedIndoor = 3000,
/// Fixed outdoor white balance 1.
kFixedOutdoor1 = 4000,
/// Fixed outdoor white balance 2.
kFixedOutdoor2 = 5000,
/// Fixed fluorescent white balance 1.
kFixedFluorescent1 = 5100,
/// Fixed fluorescent white balance 2.
kFixedFlourescent2 = 5200
};
@@ -470,10 +598,17 @@ class UsbCamera : public VideoCamera {
*/
class HttpCamera : public VideoCamera {
public:
/**
* HTTP camera kind.
*/
enum HttpCameraKind {
/// Unknown camera kind.
kUnknown = CS_HTTP_UNKNOWN,
/// MJPG Streamer camera.
kMJPGStreamer = CS_HTTP_MJPGSTREAMER,
/// CS Core camera.
kCSCore = CS_HTTP_CSCORE,
/// Axis camera.
kAxis = CS_HTTP_AXIS
};
@@ -716,8 +851,11 @@ class VideoSink {
public:
enum Kind {
/// Unknown sink type.
kUnknown = CS_SINK_UNKNOWN,
/// MJPEG video sink.
kMjpeg = CS_SINK_MJPEG,
/// CV video sink.
kCv = CS_SINK_CV
};
@@ -727,8 +865,18 @@ class VideoSink {
VideoSink& operator=(VideoSink other) noexcept;
~VideoSink();
/**
* Returns true if the VideoSink is valid.
*
* @return True if the VideoSink is valid.
*/
explicit operator bool() const { return m_handle != 0; }
/**
* Returns the VideoSink handle.
*
* @return The VideoSink handle.
*/
int GetHandle() const { return m_handle; }
bool operator==(const VideoSink& other) const {
@@ -972,17 +1120,23 @@ class ImageSink : public VideoSink {
class VideoEvent : public RawEvent {
public:
/**
* Get the source associated with the event (if any).
* Returns the source associated with the event (if any).
*
* @return The source associated with the event (if any).
*/
VideoSource GetSource() const;
/**
* Get the sink associated with the event (if any).
* Returns the sink associated with the event (if any).
*
* @return The sink associated with the event (if any).
*/
VideoSink GetSink() const;
/**
* Get the property associated with the event (if any).
* Returns the property associated with the event (if any).
*
* @return The property associated with the event (if any).
*/
VideoProperty GetProperty() const;
};

View File

@@ -7,13 +7,14 @@ package edu.wpi.first.cscore;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import edu.wpi.first.util.PixelFormat;
import org.junit.jupiter.api.Test;
class VideoModeTest {
@Test
void equalityTest() {
VideoMode a = new VideoMode(VideoMode.PixelFormat.kMJPEG, 1920, 1080, 30);
VideoMode b = new VideoMode(VideoMode.PixelFormat.kMJPEG, 1920, 1080, 30);
VideoMode a = new VideoMode(PixelFormat.kMJPEG, 1920, 1080, 30);
VideoMode b = new VideoMode(PixelFormat.kMJPEG, 1920, 1080, 30);
assertEquals(a, b);
assertNotEquals(a, null);

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,4 +1,4 @@
/*
/**
* Autogenerated file! Do not manually edit this file. This version is regenerated
* any time the publish task is run, or when this file is deleted.
*/

View File

@@ -1,72 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "DataLogThread.h"
#include <fmt/format.h>
DataLogThread::~DataLogThread() {
if (m_thread.joinable()) {
m_active = false;
m_thread.join();
}
}
void DataLogThread::ReadMain() {
for (auto record : m_reader) {
if (!m_active) {
break;
}
++m_numRecords;
if (record.IsStart()) {
wpi::log::StartRecordData data;
if (record.GetStartData(&data)) {
std::scoped_lock lock{m_mutex};
if (m_entries.find(data.entry) != m_entries.end()) {
fmt::print("...DUPLICATE entry ID, overriding\n");
}
m_entries[data.entry] = data;
m_entryNames.emplace(data.name, data);
sigEntryAdded(data);
} else {
fmt::print("Start(INVALID)\n");
}
} else if (record.IsFinish()) {
int entry;
if (record.GetFinishEntry(&entry)) {
std::scoped_lock lock{m_mutex};
auto it = m_entries.find(entry);
if (it == m_entries.end()) {
fmt::print("...ID not found\n");
} else {
m_entries.erase(it);
}
} else {
fmt::print("Finish(INVALID)\n");
}
} else if (record.IsSetMetadata()) {
wpi::log::MetadataRecordData data;
if (record.GetSetMetadataData(&data)) {
std::scoped_lock lock{m_mutex};
auto it = m_entries.find(data.entry);
if (it == m_entries.end()) {
fmt::print("...ID not found\n");
} else {
it->second.metadata = data.metadata;
auto nameIt = m_entryNames.find(it->second.name);
if (nameIt != m_entryNames.end()) {
nameIt->second.metadata = data.metadata;
}
}
} else {
fmt::print("SetMetadata(INVALID)\n");
}
} else if (record.IsControl()) {
fmt::print("Unrecognized control record\n");
}
}
sigDone();
m_done = true;
}

View File

@@ -1,71 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <atomic>
#include <functional>
#include <map>
#include <string>
#include <string_view>
#include <thread>
#include <utility>
#include <wpi/DataLogReader.h>
#include <wpi/DenseMap.h>
#include <wpi/Signal.h>
#include <wpi/mutex.h>
class DataLogThread {
public:
explicit DataLogThread(wpi::log::DataLogReader reader)
: m_reader{std::move(reader)}, m_thread{[=, this] { ReadMain(); }} {}
~DataLogThread();
bool IsDone() const { return m_done; }
std::string_view GetBufferIdentifier() const {
return m_reader.GetBufferIdentifier();
}
unsigned int GetNumRecords() const { return m_numRecords; }
unsigned int GetNumEntries() const {
std::scoped_lock lock{m_mutex};
return m_entryNames.size();
}
// Passes wpi::log::StartRecordData to func
template <typename T>
void ForEachEntryName(T&& func) {
std::scoped_lock lock{m_mutex};
for (auto&& kv : m_entryNames) {
func(kv.second);
}
}
wpi::log::StartRecordData GetEntry(std::string_view name) const {
std::scoped_lock lock{m_mutex};
auto it = m_entryNames.find(name);
if (it == m_entryNames.end()) {
return {};
}
return it->second;
}
const wpi::log::DataLogReader& GetReader() const { return m_reader; }
// note: these are called on separate thread
wpi::sig::Signal_mt<const wpi::log::StartRecordData&> sigEntryAdded;
wpi::sig::Signal_mt<> sigDone;
private:
void ReadMain();
wpi::log::DataLogReader m_reader;
mutable wpi::mutex m_mutex;
std::atomic_bool m_active{true};
std::atomic_bool m_done{false};
std::atomic<unsigned int> m_numRecords{0};
std::map<std::string, wpi::log::StartRecordData, std::less<>> m_entryNames;
wpi::DenseMap<int, wpi::log::StartRecordData> m_entries;
std::thread m_thread;
};

View File

@@ -27,7 +27,7 @@
Downloader::Downloader(glass::Storage& storage)
: m_serverTeam{storage.GetString("serverTeam")},
m_remoteDir{storage.GetString("remoteDir", "/home/lvuser")},
m_remoteDir{storage.GetString("remoteDir", "/home/lvuser/logs")},
m_username{storage.GetString("username", "lvuser")},
m_localDir{storage.GetString("localDir")},
m_deleteAfter{storage.GetBool("deleteAfter", true)},

View File

@@ -17,6 +17,7 @@
#include <fmt/chrono.h>
#include <fmt/format.h>
#include <glass/Storage.h>
#include <glass/support/DataLogReaderThread.h>
#include <imgui.h>
#include <imgui_internal.h>
#include <imgui_stdlib.h>
@@ -32,11 +33,10 @@
#include <wpi/raw_ostream.h>
#include "App.h"
#include "DataLogThread.h"
namespace {
struct InputFile {
explicit InputFile(std::unique_ptr<DataLogThread> datalog);
explicit InputFile(std::unique_ptr<glass::DataLogReaderThread> datalog);
InputFile(std::string_view filename, std::string_view status)
: filename{filename},
@@ -47,7 +47,7 @@ struct InputFile {
std::string filename;
std::string stem;
std::unique_ptr<DataLogThread> datalog;
std::unique_ptr<glass::DataLogReaderThread> datalog;
std::string status;
bool highlight = false;
};
@@ -135,7 +135,7 @@ static void RebuildEntryTree() {
}
}
InputFile::InputFile(std::unique_ptr<DataLogThread> datalog_)
InputFile::InputFile(std::unique_ptr<glass::DataLogReaderThread> datalog_)
: filename{datalog_->GetBufferIdentifier()},
stem{fs::path{filename}.stem().string()},
datalog{std::move(datalog_)} {
@@ -192,7 +192,7 @@ static std::unique_ptr<InputFile> LoadDataLog(std::string_view filename) {
}
return std::make_unique<InputFile>(
std::make_unique<DataLogThread>(std::move(reader)));
std::make_unique<glass::DataLogReaderThread>(std::move(reader)));
}
void DisplayInputFiles() {
@@ -284,9 +284,10 @@ static bool EmitEntry(const std::string& name, Entry& entry) {
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
for (auto inputFile : entry.inputFiles) {
ImGui::Text(
"%s: %s", inputFile->stem.c_str(),
std::string{inputFile->datalog->GetEntry(entry.name).type}.c_str());
if (auto info = inputFile->datalog->GetEntry(entry.name)) {
ImGui::Text("%s: %s", inputFile->stem.c_str(),
std::string{info->type}.c_str());
}
}
ImGui::EndTooltip();
}
@@ -300,10 +301,10 @@ static bool EmitEntry(const std::string& name, Entry& entry) {
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
for (auto inputFile : entry.inputFiles) {
ImGui::Text(
"%s: %s", inputFile->stem.c_str(),
std::string{inputFile->datalog->GetEntry(entry.name).metadata}
.c_str());
if (auto info = inputFile->datalog->GetEntry(entry.name)) {
ImGui::Text("%s: %s", inputFile->stem.c_str(),
std::string{info->metadata}.c_str());
}
}
ImGui::EndTooltip();
}

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

@@ -208,21 +208,27 @@ configurations {
}
}
ext {
sharedCvConfigs = [:]
staticCvConfigs = [:]
useJava = true
useCpp = false
skipDev = true
useDocumentation = true
}
apply from: "${rootDir}/shared/opencv.gradle"
task generateJavaDocs(type: Javadoc) {
classpath += project(":wpimath").sourceSets.main.compileClasspath
classpath += project(":wpilibj").sourceSets.main.compileClasspath
options.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
options.links("https://docs.opencv.org/4.x/javadoc/")
options.addStringOption("tag", "pre:a:Pre-Condition")
options.addBooleanOption("Xdoclint/package:" +
// TODO: v Document these, then remove them from the list
"-edu.wpi.first.hal," +
"-edu.wpi.first.hal.can," +
"-edu.wpi.first.hal.simulation," +
// TODO: ^ Document these, then remove them from the list
"-edu.wpi.first.math.proto," +
"-edu.wpi.first.math.controller.proto," +
"-edu.wpi.first.math.controller.struct," +
"-edu.wpi.first.math.geometry.proto," +
"-edu.wpi.first.math.geometry.struct," +
"-edu.wpi.first.math.kinematics.proto," +
"-edu.wpi.first.math.kinematics.struct," +
"-edu.wpi.first.math.system.plant.proto," +
"-edu.wpi.first.math.system.plant.struct," +
"-edu.wpi.first.math.trajectory.proto", true)
options.addBooleanOption("Xdoclint:html,missing,reference,syntax", true)
options.addBooleanOption('html5', true)
options.linkSource(true)

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

@@ -15,12 +15,13 @@ public enum Fields {
k2021GalacticSearchB("2021-galacticsearchb.json"),
k2021Slalom("2021-slalompath.json"),
k2022RapidReact("2022-rapidreact.json"),
k2023ChargedUp("2023-chargedup.json");
k2023ChargedUp("2023-chargedup.json"),
k2024Crescendo("2024-crescendo.json");
public static final String kBaseResourceDir = "/edu/wpi/first/fields/";
/** Alias to the current game. */
public static final Fields kDefaultField = k2023ChargedUp;
public static final Fields kDefaultField = k2024Crescendo;
public final String m_resourceFile;

View File

@@ -15,10 +15,13 @@
#include "fields/2021-slalom.h"
#include "fields/2022-rapidreact.h"
#include "fields/2023-chargedup.h"
#include "fields/2024-crescendo.h"
using namespace fields;
static const Field kFields[] = {
{"2024 Crescendo", GetResource_2024_crescendo_json,
GetResource_2024_field_png},
{"2023 Charged Up", GetResource_2023_chargedup_json,
GetResource_2023_field_png},
{"2022 Rapid React", GetResource_2022_rapidreact_json,

View File

@@ -0,0 +1,12 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <string_view>
namespace fields {
std::string_view GetResource_2024_crescendo_json();
std::string_view GetResource_2024_field_png();
} // namespace fields

View File

@@ -0,0 +1,19 @@
{
"game": "Crescendo",
"field-image": "2024-field.png",
"field-corners": {
"top-left": [
150,
79
],
"bottom-right": [
2961,
1476
]
},
"field-size": [
54.27083,
26.9375
],
"field-unit": "foot"
}

Binary file not shown.

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

@@ -1,4 +1,4 @@
/*
/**
* Autogenerated file! Do not manually edit this file. This version is regenerated
* any time the publish task is run, or when this file is deleted.
*/

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

@@ -178,8 +178,6 @@ static bool LoadStorageRootImpl(Context* ctx, const std::string& filename,
static bool LoadStorageImpl(Context* ctx, std::string_view dir,
std::string_view name) {
WorkspaceResetImpl();
bool rv = true;
for (auto&& root : ctx->storageRoots) {
std::string filename;
@@ -421,6 +419,7 @@ std::string glass::GetStorageDir() {
bool glass::LoadStorage(std::string_view dir) {
SaveStorage();
SetStorageDir(dir);
WorkspaceResetImpl();
LoadWindowStorageImpl((fs::path{gContext->storageLoadDir} /
fmt::format("{}-window.json", gContext->storageName))
.string());

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

@@ -29,6 +29,16 @@ void glass::DisplayPIDController(PIDControllerModel* m) {
callback(*v);
}
};
// Workaround to allow for the input of inf, -inf, and nan
auto createTuningParameterNoFilter =
[flag](const char* name, double* v,
std::function<void(double)> callback) {
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
if (ImGui::InputScalar(name, ImGuiDataType_Double, v, NULL, NULL,
"%.3f", flag)) {
callback(*v);
}
};
if (auto p = m->GetPData()) {
double value = p->GetValue();
@@ -47,6 +57,11 @@ void glass::DisplayPIDController(PIDControllerModel* m) {
createTuningParameter("Setpoint", &value,
[=](auto v) { m->SetSetpoint(v); });
}
if (auto s = m->GetIZoneData()) {
double value = s->GetValue();
createTuningParameterNoFilter("IZone", &value,
[=](auto v) { m->SetIZone(v); });
}
} else {
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(96, 96, 96, 255));
ImGui::Text("Unknown PID Controller");

View File

@@ -0,0 +1,69 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "glass/other/ProfiledPIDController.h"
#include <string>
#include <imgui.h>
#include "glass/Context.h"
#include "glass/DataSource.h"
using namespace glass;
void glass::DisplayProfiledPIDController(ProfiledPIDControllerModel* m) {
if (auto name = m->GetName()) {
ImGui::Text("%s", name);
ImGui::Separator();
}
if (m->Exists()) {
auto flag = m->IsReadOnly() ? ImGuiInputTextFlags_ReadOnly
: ImGuiInputTextFlags_None;
auto createTuningParameter = [flag](const char* name, double* v,
std::function<void(double)> callback) {
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
if (ImGui::InputDouble(name, v, 0.0, 0.0, "%.3f", flag)) {
callback(*v);
}
};
// Workaround to allow for the input of inf, -inf, and nan
auto createTuningParameterNoFilter =
[flag](const char* name, double* v,
std::function<void(double)> callback) {
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
if (ImGui::InputScalar(name, ImGuiDataType_Double, v, NULL, NULL,
"%.3f", flag)) {
callback(*v);
}
};
if (auto p = m->GetPData()) {
double value = p->GetValue();
createTuningParameter("P", &value, [=](auto v) { m->SetP(v); });
}
if (auto i = m->GetIData()) {
double value = i->GetValue();
createTuningParameter("I", &value, [=](auto v) { m->SetI(v); });
}
if (auto d = m->GetDData()) {
double value = d->GetValue();
createTuningParameter("D", &value, [=](auto v) { m->SetD(v); });
}
if (auto s = m->GetGoalData()) {
double value = s->GetValue();
createTuningParameter("Goal", &value, [=](auto v) { m->SetGoal(v); });
}
if (auto s = m->GetIZoneData()) {
double value = s->GetValue();
createTuningParameterNoFilter("IZone", &value,
[=](auto v) { m->SetIZone(v); });
}
} else {
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(96, 96, 96, 255));
ImGui::Text("Unknown PID Controller");
ImGui::PopStyleColor();
}
}

View File

@@ -0,0 +1,124 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "glass/support/DataLogReaderThread.h"
#include <utility>
#include <fmt/format.h>
#include <wpi/StringExtras.h>
using namespace glass;
DataLogReaderThread::~DataLogReaderThread() {
if (m_thread.joinable()) {
m_active = false;
m_thread.join();
}
}
void DataLogReaderThread::ReadMain() {
wpi::SmallDenseMap<
int, std::pair<DataLogReaderEntry*, std::span<const uint8_t>>, 8>
schemaEntries;
for (auto recordIt = m_reader.begin(), recordEnd = m_reader.end();
recordIt != recordEnd; ++recordIt) {
auto& record = *recordIt;
if (!m_active) {
break;
}
++m_numRecords;
if (record.IsStart()) {
DataLogReaderEntry data;
if (record.GetStartData(&data)) {
std::scoped_lock lock{m_mutex};
auto& entryPtr = m_entriesById[data.entry];
if (entryPtr) {
fmt::print("...DUPLICATE entry ID, overriding\n");
}
auto [it, isNew] = m_entriesByName.emplace(data.name, data);
if (isNew) {
it->second.ranges.emplace_back(recordIt, recordEnd);
}
entryPtr = &it->second;
if (data.type == "structschema" ||
data.type == "proto:FileDescriptorProto") {
schemaEntries.try_emplace(data.entry, entryPtr,
std::span<const uint8_t>{});
}
sigEntryAdded(data);
} else {
fmt::print("Start(INVALID)\n");
}
} else if (record.IsFinish()) {
int entry;
if (record.GetFinishEntry(&entry)) {
std::scoped_lock lock{m_mutex};
auto it = m_entriesById.find(entry);
if (it == m_entriesById.end()) {
fmt::print("...ID not found\n");
} else {
it->second->ranges.back().m_end = recordIt;
m_entriesById.erase(it);
}
} else {
fmt::print("Finish(INVALID)\n");
}
} else if (record.IsSetMetadata()) {
wpi::log::MetadataRecordData data;
if (record.GetSetMetadataData(&data)) {
std::scoped_lock lock{m_mutex};
auto it = m_entriesById.find(data.entry);
if (it == m_entriesById.end()) {
fmt::print("...ID not found\n");
} else {
it->second->metadata = data.metadata;
}
} else {
fmt::print("SetMetadata(INVALID)\n");
}
} else if (record.IsControl()) {
fmt::print("Unrecognized control record\n");
} else {
auto it = schemaEntries.find(record.GetEntry());
if (it != schemaEntries.end()) {
it->second.second = record.GetRaw();
}
}
}
// build schema databases
for (auto&& schemaPair : schemaEntries) {
auto name = schemaPair.second.first->name;
auto data = schemaPair.second.second;
if (data.empty()) {
continue;
}
if (wpi::starts_with(name, "NT:")) {
name = wpi::drop_front(name, 3);
}
if (wpi::starts_with(name, "/.schema/struct:")) {
auto typeStr = wpi::drop_front(name, 16);
std::string_view schema{reinterpret_cast<const char*>(data.data()),
data.size()};
std::string err;
auto desc = m_structDb.Add(typeStr, schema, &err);
if (!desc) {
fmt::print("could not decode struct '{}' schema '{}': {}\n", name,
schema, err);
}
} else if (wpi::starts_with(name, "/.schema/proto:")) {
// protobuf descriptor handling
auto filename = wpi::drop_front(name, 15);
if (!m_protoDb.Add(filename, data)) {
fmt::print("could not decode protobuf '{}' filename '{}'\n", name,
filename);
}
}
}
sigDone();
m_done = true;
}

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

@@ -16,11 +16,13 @@ class PIDControllerModel : public Model {
virtual DataSource* GetIData() = 0;
virtual DataSource* GetDData() = 0;
virtual DataSource* GetSetpointData() = 0;
virtual DataSource* GetIZoneData() = 0;
virtual void SetP(double value) = 0;
virtual void SetI(double value) = 0;
virtual void SetD(double value) = 0;
virtual void SetSetpoint(double value) = 0;
virtual void SetIZone(double value) = 0;
};
void DisplayPIDController(PIDControllerModel* m);
} // namespace glass

View File

@@ -0,0 +1,28 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include "glass/Model.h"
namespace glass {
class DataSource;
class ProfiledPIDControllerModel : public Model {
public:
virtual const char* GetName() const = 0;
virtual DataSource* GetPData() = 0;
virtual DataSource* GetIData() = 0;
virtual DataSource* GetDData() = 0;
virtual DataSource* GetGoalData() = 0;
virtual DataSource* GetIZoneData() = 0;
virtual void SetP(double value) = 0;
virtual void SetI(double value) = 0;
virtual void SetD(double value) = 0;
virtual void SetGoal(double value) = 0;
virtual void SetIZone(double value) = 0;
};
void DisplayProfiledPIDController(ProfiledPIDControllerModel* m);
} // namespace glass

View File

@@ -0,0 +1,101 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <atomic>
#include <functional>
#include <map>
#include <string>
#include <string_view>
#include <thread>
#include <utility>
#include <vector>
#include <wpi/DataLogReader.h>
#include <wpi/DenseMap.h>
#include <wpi/Signal.h>
#include <wpi/mutex.h>
#include <wpi/protobuf/ProtobufMessageDatabase.h>
#include <wpi/struct/DynamicStruct.h>
namespace glass {
class DataLogReaderRange {
public:
DataLogReaderRange(wpi::log::DataLogReader::iterator begin,
wpi::log::DataLogReader::iterator end)
: m_begin{begin}, m_end{end} {}
wpi::log::DataLogReader::iterator begin() const { return m_begin; }
wpi::log::DataLogReader::iterator end() const { return m_end; }
wpi::log::DataLogReader::iterator m_begin;
wpi::log::DataLogReader::iterator m_end;
};
class DataLogReaderEntry : public wpi::log::StartRecordData {
public:
std::vector<DataLogReaderRange> ranges; // ranges where this entry is valid
};
class DataLogReaderThread {
public:
explicit DataLogReaderThread(wpi::log::DataLogReader reader)
: m_reader{std::move(reader)}, m_thread{[this] { ReadMain(); }} {}
~DataLogReaderThread();
bool IsDone() const { return m_done; }
std::string_view GetBufferIdentifier() const {
return m_reader.GetBufferIdentifier();
}
unsigned int GetNumRecords() const { return m_numRecords; }
unsigned int GetNumEntries() const {
std::scoped_lock lock{m_mutex};
return m_entriesByName.size();
}
// Passes Entry& to func
template <typename T>
void ForEachEntryName(T&& func) {
std::scoped_lock lock{m_mutex};
for (auto&& kv : m_entriesByName) {
func(kv.second);
}
}
const DataLogReaderEntry* GetEntry(std::string_view name) const {
std::scoped_lock lock{m_mutex};
auto it = m_entriesByName.find(name);
if (it == m_entriesByName.end()) {
return nullptr;
}
return &it->second;
}
wpi::StructDescriptorDatabase& GetStructDatabase() { return m_structDb; }
wpi::ProtobufMessageDatabase& GetProtobufDatabase() { return m_protoDb; }
const wpi::log::DataLogReader& GetReader() const { return m_reader; }
// note: these are called on separate thread
wpi::sig::Signal_mt<const DataLogReaderEntry&> sigEntryAdded;
wpi::sig::Signal_mt<> sigDone;
private:
void ReadMain();
wpi::log::DataLogReader m_reader;
mutable wpi::mutex m_mutex;
std::atomic_bool m_active{true};
std::atomic_bool m_done{false};
std::atomic<unsigned int> m_numRecords{0};
std::map<std::string, DataLogReaderEntry, std::less<>> m_entriesByName;
wpi::DenseMap<int, DataLogReaderEntry*> m_entriesById;
wpi::StructDescriptorDatabase m_structDb;
wpi::ProtobufMessageDatabase m_protoDb;
std::thread m_thread;
};
} // namespace glass

View File

@@ -23,10 +23,12 @@ NTPIDControllerModel::NTPIDControllerModel(nt::NetworkTableInstance inst,
m_d{inst.GetDoubleTopic(fmt::format("{}/d", path)).GetEntry(0)},
m_setpoint{
inst.GetDoubleTopic(fmt::format("{}/setpoint", path)).GetEntry(0)},
m_iZone{inst.GetDoubleTopic(fmt::format("{}/izone", path)).GetEntry(0)},
m_pData{fmt::format("NTPIDCtrlP:{}", path)},
m_iData{fmt::format("NTPIDCtrlI:{}", path)},
m_dData{fmt::format("NTPIDCtrlD:{}", path)},
m_setpointData{fmt::format("NTPIDCtrlStpt:{}", path)},
m_iZoneData{fmt::format("NTPIDCtrlIZone:{}", path)},
m_nameValue{wpi::rsplit(path, '/').second} {}
void NTPIDControllerModel::SetP(double value) {
@@ -44,6 +46,9 @@ void NTPIDControllerModel::SetD(double value) {
void NTPIDControllerModel::SetSetpoint(double value) {
m_setpoint.Set(value);
}
void NTPIDControllerModel::SetIZone(double value) {
m_iZone.Set(value);
}
void NTPIDControllerModel::Update() {
for (auto&& v : m_name.ReadQueue()) {
@@ -61,6 +66,9 @@ void NTPIDControllerModel::Update() {
for (auto&& v : m_setpoint.ReadQueue()) {
m_setpointData.SetValue(v.value, v.time);
}
for (auto&& v : m_iZone.ReadQueue()) {
m_iZoneData.SetValue(v.value, v.time);
}
for (auto&& v : m_controllable.ReadQueue()) {
m_controllableValue = v.value;
}

View File

@@ -0,0 +1,80 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "glass/networktables/NTProfiledPIDController.h"
#include <fmt/format.h>
#include <wpi/StringExtras.h>
using namespace glass;
NTProfiledPIDControllerModel::NTProfiledPIDControllerModel(
std::string_view path)
: NTProfiledPIDControllerModel(nt::NetworkTableInstance::GetDefault(),
path) {}
NTProfiledPIDControllerModel::NTProfiledPIDControllerModel(
nt::NetworkTableInstance inst, std::string_view path)
: m_inst{inst},
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
m_controllable{inst.GetBooleanTopic(fmt::format("{}/.controllable", path))
.Subscribe(false)},
m_p{inst.GetDoubleTopic(fmt::format("{}/p", path)).GetEntry(0)},
m_i{inst.GetDoubleTopic(fmt::format("{}/i", path)).GetEntry(0)},
m_d{inst.GetDoubleTopic(fmt::format("{}/d", path)).GetEntry(0)},
m_goal{inst.GetDoubleTopic(fmt::format("{}/goal", path)).GetEntry(0)},
m_iZone{inst.GetDoubleTopic(fmt::format("{}/izone", path)).GetEntry(0)},
m_pData{fmt::format("NTPIDCtrlP:{}", path)},
m_iData{fmt::format("NTPIDCtrlI:{}", path)},
m_dData{fmt::format("NTPIDCtrlD:{}", path)},
m_goalData{fmt::format("NTPIDCtrlGoal:{}", path)},
m_iZoneData{fmt::format("NTPIDCtrlIZone:{}", path)},
m_nameValue{wpi::rsplit(path, '/').second} {}
void NTProfiledPIDControllerModel::SetP(double value) {
m_p.Set(value);
}
void NTProfiledPIDControllerModel::SetI(double value) {
m_i.Set(value);
}
void NTProfiledPIDControllerModel::SetD(double value) {
m_d.Set(value);
}
void NTProfiledPIDControllerModel::SetGoal(double value) {
m_goal.Set(value);
}
void NTProfiledPIDControllerModel::SetIZone(double value) {
m_iZone.Set(value);
}
void NTProfiledPIDControllerModel::Update() {
for (auto&& v : m_name.ReadQueue()) {
m_nameValue = std::move(v.value);
}
for (auto&& v : m_p.ReadQueue()) {
m_pData.SetValue(v.value, v.time);
}
for (auto&& v : m_i.ReadQueue()) {
m_iData.SetValue(v.value, v.time);
}
for (auto&& v : m_d.ReadQueue()) {
m_dData.SetValue(v.value, v.time);
}
for (auto&& v : m_goal.ReadQueue()) {
m_goalData.SetValue(v.value, v.time);
}
for (auto&& v : m_iZone.ReadQueue()) {
m_iZoneData.SetValue(v.value, v.time);
}
for (auto&& v : m_controllable.ReadQueue()) {
m_controllableValue = v.value;
}
}
bool NTProfiledPIDControllerModel::Exists() {
return m_goal.Exists();
}

View File

@@ -5,6 +5,7 @@
#include "glass/networktables/NTStringChooser.h"
#include <fmt/format.h>
#include <wpi/json.h>
using namespace glass;
@@ -16,17 +17,17 @@ NTStringChooserModel::NTStringChooserModel(nt::NetworkTableInstance inst,
: m_inst{inst},
m_default{
m_inst.GetStringTopic(fmt::format("{}/default", path)).Subscribe("")},
m_selected{
m_inst.GetStringTopic(fmt::format("{}/selected", path)).GetEntry("")},
m_selected{m_inst.GetStringTopic(fmt::format("{}/selected", path))
.Subscribe("")},
m_selectedPub{m_inst.GetStringTopic(fmt::format("{}/selected", path))
.PublishEx("string", {{"retained", true}})},
m_active{
m_inst.GetStringTopic(fmt::format("{}/active", path)).Subscribe("")},
m_options{m_inst.GetStringArrayTopic(fmt::format("{}/options", path))
.Subscribe({})} {
m_selected.GetTopic().SetRetained(true);
}
.Subscribe({})} {}
void NTStringChooserModel::SetSelected(std::string_view val) {
m_selected.Set(val);
m_selectedPub.Set(val);
}
void NTStringChooserModel::Update() {

View File

@@ -879,7 +879,10 @@ void NetworkTablesModel::Update() {
} else if (desc->IsValid()) {
// loop over all entries with this type and update
for (auto&& entryPair : m_entries) {
auto ts = entryPair.second->info.type_str;
if (!entryPair.second) {
continue;
}
std::string_view ts = entryPair.second->info.type_str;
if (!wpi::starts_with(ts, "struct:")) {
continue;
}
@@ -901,7 +904,10 @@ void NetworkTablesModel::Update() {
} else {
// loop over all protobuf entries and update (conservatively)
for (auto&& entryPair : m_entries) {
auto& ts = entryPair.second->info.type_str;
if (!entryPair.second) {
continue;
}
std::string_view ts = entryPair.second->info.type_str;
if (wpi::starts_with(ts, "proto:")) {
entryPair.second->UpdateFromValue(*this);
}

View File

@@ -83,6 +83,18 @@ void NetworkTablesProvider::DisplayMenu() {
// FIXME: enabled?
// data is the last item, so is guaranteed to be null-terminated
ImGui::MenuItem(path.back().data(), nullptr, &visible, true);
// Add type label to smartdashboard sendables
if (wpi::starts_with(entry->name, "/SmartDashboard/")) {
auto typeEntry = m_typeCache.FindValue(entry->name);
if (typeEntry) {
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(96, 96, 96, 255));
ImGui::Text("%s", typeEntry->stringVal.c_str());
ImGui::PopStyleColor();
ImGui::SameLine();
ImGui::Dummy(ImVec2(10.0f, 0.0f));
}
}
if (!wasVisible && visible) {
Show(entry.get(), entry->window);
} else if (wasVisible && !visible && entry->window) {

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