Compare commits

...

142 Commits

Author SHA1 Message Date
sciencewhiz
0a3ccf93c6 [wpimath] Add BangBangController Usage Reporting (#7411) 2024-11-20 17:00:54 -08:00
Matt
d92f17b014 [apriltag] Fix AprilTagDetector cols/stride mixup (#7415) 2024-11-20 17:00:08 -08:00
Gold856
4c225ef2c1 [wpimath] Add proto files back to JAR (#7414)
PhotonVision depends on these being in the JAR to generate quickbuf classes.
2024-11-20 07:59:37 -08:00
Thad House
561078ce29 [hal] Cache sim TCP data to update during HAL_RefreshDSData (#7360) 2024-11-18 20:56:32 -08:00
sciencewhiz
d312bccfeb [hal] Add Swerve Instances for RobotDrive usage reporting (#7410) 2024-11-18 20:51:50 -08:00
David Vo
9826539198 [hal] Add MagicBot framework to usage reporting (#7330)
Co-authored-by: sciencewhiz <sciencewhiz@users.noreply.github.com>
2024-11-18 20:51:16 -08:00
sciencewhiz
33f7067216 [hal] Add usage reporting for Rust (#7409)
Co-authored-by: itsmeft24 <57544858+itsmeft24@users.noreply.github.com>
2024-11-18 20:25:33 -08:00
Vignesh Balasubramaniam
ac1836ec44 [wpiunits] Add absolute value and copy sign functionality (#7358) 2024-11-18 17:46:15 -08:00
sciencewhiz
57e10755fd [wpilib] Add usage reporting for dashboards as instances (#7294)
Detects dashboards based on network tables client identity.
2024-11-18 10:16:29 -07:00
Tyler Veness
8ec22b7d5c [wpiutil] Remove unfinished ct_map class (#7406) 2024-11-17 21:31:25 -08:00
Tyler Veness
a04c40f589 Replace std::make_pair with std::pair CTAD (#7405) 2024-11-17 20:29:23 -08:00
Jade
b4bec566f0 [github] Fix templates not labelling (#7400) 2024-11-17 20:28:39 -08:00
sciencewhiz
d76827db48 [build] Update to Gradle 8.11 (#7402) 2024-11-17 20:28:12 -08:00
Tyler Veness
602c4caa02 [upstream_utils] Check patch files are up to date (#7401) 2024-11-17 20:27:53 -08:00
Peter Johnson
661c321fe2 [upstream_utils] Restore Eigen intellisense fix (#7404)
This reverts commit d1de7663d3.

Intellisense is still broken on Windows Athena target with the error
`incomplete type "Eigen::Matrix<double, 3, 3, 0, 3, 3>" is not allowed`.
2024-11-17 19:11:25 -08:00
Tyler Veness
d1de7663d3 [upstream_utils] Remove Eigen intellisense fix (#7397)
FRC Code intellisense continued to work in developerRobot with a QR
decomposition.
2024-11-16 22:16:11 -08:00
Peter Johnson
b040059108 [ntcore] Properly clean up time sync listeners (#7398) 2024-11-16 22:15:40 -08:00
sciencewhiz
0798ac53d0 [build] Generate NI Style UsageReporting header (#7331)
based on wplibsuite/ni-libraries src/include/FRC_NetworkCommunication/UsageReporting.h
2024-11-16 07:58:25 -08:00
Sam Carlberg
ded7c87d63 [wpimath] Remove units from trapezoid profile classes (#7276) 2024-11-16 07:57:38 -08:00
Joseph Eng
2acf111f56 [wpimath] Add 3D odometry and pose estimation (#7119) 2024-11-16 07:56:14 -08:00
Tyler Veness
aa7dd258c4 [wpimath] Replace constexpr coeff() and coeffRef() with operator() (#7391) 2024-11-16 07:44:20 -08:00
Jonah Bonner
ca51197486 [wpilib] Add timestamp getters with configurable time base (#7378) 2024-11-16 07:43:38 -08:00
Gold856
91142ba5fe [build] Remove Windows constexpr mutex define (#7375)
Since we ship a newer runtime, and we also have checks to ensure a valid runtime, we can remove this again.
2024-11-16 07:27:46 -08:00
Thad House
969664ceaa [wpiutil] Faster nanopb submessage encode (#7374)
Due to how submessages are encoded (with a length prefix), nanopb currently does the encoding twice. It encodes once to get the length to write, then writes the length, then reencodes the entire message a 2nd time.

This results in a requirement that each encode always encodes the same. Generally, this is fine, but it'd be nice to not make this a requirement.

The double encode also requires going through the entire set of fields again, which has the possibility to be slow.

Instead of doing this, write to a temporary SmallVector. Then we can just encode the length of that buffer, and do a memcpy into primary stream. Theoretically in most cases, this should be much faster.
2024-11-16 07:26:10 -08:00
Kavin Muralikrishnan
1e545c38a8 [romi] Update Romi for parity with XRP (#7389) 2024-11-16 07:24:58 -08:00
Kavin Muralikrishnan
6eb652e10e [xrp] Copy XRPReference docs from Java to C++ (NFC) (#7388) 2024-11-16 07:24:21 -08:00
Thad House
fff73ee6e1 [ci] Add basic Android build to CI (#7390) 2024-11-16 07:23:32 -08:00
Tim Winters
bade0a8716 [wpiunits] Use div instead of divide for kotlin compatibility (#7387) 2024-11-15 11:49:40 -07:00
Thad House
453335e354 [build] Remove java requirement from cmake (#7395)
It was leftover from the failed protoc stuff
2024-11-15 07:45:20 -07:00
Thad House
c289562a06 Allow compilation on android (#7386) 2024-11-12 16:39:41 -08:00
Kavin Muralikrishnan
07345712dc [wpimath] Document default tolerances of PIDController (#7377) 2024-11-12 18:19:55 -05:00
Joseph Eng
425bf83036 [wpimath] Add 2D to 3D geometry constructors (#7380) 2024-11-12 18:18:37 -05:00
David Vo
6adad7bad7 [wpiutil] Replace StringBuffer usage with StringBuilder (#7376)
This is a local variable that doesn't escape the method, so there's certainly no reason to have synchronization here.
2024-11-10 07:43:43 -08:00
Kavin Muralikrishnan
280d2c7e32 [examples] Update C++ XRP Code to use SI Units (#7366) 2024-11-08 20:24:13 -08:00
Thad House
edc3963955 [wpinet] Fix resolver thread on newer versions of macOS (#7372)
Implicit capture of this is deprecated.
2024-11-08 20:23:17 -08:00
Tyler Veness
c58be2580d [ntcore] Suppress warning false positive (#7370)
```
In copy constructor ‘std::function<_Res(_ArgTypes ...)>::function(const std::function<_Res(_ArgTypes ...)>&) [with _Res = void; _ArgTypes = {unsigned int}]’,
    inlined from ‘nt::server::ServerClient4Base::ServerClient4Base(std::string_view, std::string_view, bool, nt::server::SetPeriodicFunc, nt::server::ServerStorage&, int, wpi::Logger&)’ at /home/tav/frc/wpilib/allwpilib/ntcore/src/main/native/cpp/server/ServerClient4Base.h:24:77,
    inlined from ‘nt::server::ServerClientLocal::ServerClientLocal(nt::server::ServerStorage&, int, wpi::Logger&)’ at /home/tav/frc/wpilib/allwpilib/ntcore/src/main/native/cpp/server/ServerClientLocal.cpp:18:75:
/usr/include/c++/14.2.1/bits/std_function.h:391:17: error: ‘<anonymous>’ may be used uninitialized [-Werror=maybe-uninitialized]
  391 |             __x._M_manager(_M_functor, __x._M_functor, __clone_functor);
      |             ~~~~^~~~~~~~~~
/usr/include/c++/14.2.1/bits/std_function.h: In constructor ‘nt::server::ServerClientLocal::ServerClientLocal(nt::server::ServerStorage&, int, wpi::Logger&)’:
/usr/include/c++/14.2.1/bits/std_function.h:267:7: note: by argument 2 of type ‘const std::_Any_data&’ to ‘static bool std::_Function_handler<_Res(_ArgTypes ...), _Functor>::_M_manager(std::_Any_data&, const std::_Any_data&, std::_Manager_operation) [with _Res = void; _Functor = nt::server::ServerClientLocal::ServerClientLocal(nt::server::ServerStorage&, int, wpi::Logger&)::<lambda(uint32_t)>; _ArgTypes = {unsigned int}]’ declared here
  267 |       _M_manager(_Any_data& __dest, const _Any_data& __source,
      |       ^~~~~~~~~~
/home/tav/frc/wpilib/allwpilib/ntcore/src/main/native/cpp/server/ServerClientLocal.cpp:18:75: note: ‘<anonymous>’ declared here
   18 |     : ServerClient4Base{"", "", true, [](uint32_t) {}, storage, id, logger} {
      |                                                                           ^
```
2024-11-08 20:22:47 -08:00
Peter Johnson
f40bd3593d [wpilib,wpimath] Don't use mutable units for return values (#7369)
It only saves a single allocation and can cause confusing behavior on the
caller (user) side.
2024-11-08 18:29:51 -08:00
Thad House
3cc541f261 Remove generated google protobuf support (#7371)
It's not used anymore, and cleans up the build.
2024-11-08 18:29:30 -08:00
Tyler Veness
811b130968 [docs] Link to Bazel build docs from main readme (#7367)
I had a really hard to finding them because the main readme didn't call
it out.
2024-11-08 14:05:50 -08:00
Thad House
d39dfd64ea [build] Fix cmake build with WITH_PROTOBUF off (#7368)
Protobuf.cpp only uses nanopb, which is fine to have even with WITH_PROTOBUF off.
2024-11-08 14:05:29 -08:00
Tyler Veness
661bae568f [wpimath] Add time-varying RKDP (#7362)
This makes the ground truth for the Taylor series AQ discretization more
accurate.
2024-11-07 23:46:52 -08:00
Peter Johnson
01f85abcfe [ntcore] Use Endian.h in WireEncoder3 2024-11-08 00:46:27 -07:00
Peter Johnson
396f8203ac [ntcore] HandleMap: Use concepts for T 2024-11-08 00:46:27 -07:00
Peter Johnson
4a43ddbacf [ntcore] Split LocalStorage implementation into separate files 2024-11-08 00:46:27 -07:00
Peter Johnson
0921054a28 [ntcore] Split ServerImpl implementation into separate files 2024-11-08 00:46:27 -07:00
Peter Johnson
f738fc92f0 [ntcore] Move ServerImpl to nt::server namespace 2024-11-08 00:46:27 -07:00
Peter Johnson
a0f38f83f9 [ntcore] NetworkOutgoingQueue: Move function defs inside class 2024-11-08 00:46:27 -07:00
Peter Johnson
876be30724 [ntcore] Value: Inline constructors 2024-11-08 00:46:27 -07:00
Peter Johnson
de318fab91 [ntcore] LocalStorage: Move template functions inside class definition 2024-11-08 00:46:27 -07:00
Thad House
8b8b634f65 [wpiutil] Change C++ protobuf to nanopb (#7309)
The Google C++ protobuf implementation has issues with dynamic linkage across DLL boundaries because it uses global variables.  It also has a compile-time dependency because the protoc version must exactly match the libprotobuf version.  Using nanopb with a customized generator fixes both of these issues.

Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>
2024-11-07 22:42:50 -08:00
Thad House
fd2e0c0427 [ntcoreffi] Switch ntcoreffi DLM to WPI_String (#7359)
We forgot to do this originally.
2024-11-07 15:19:37 -07:00
Tyler Veness
a66fa339dc [wpimath] Make controllers and some trajectory classes constexpr (#7343) 2024-11-07 14:02:11 -07:00
Kavin Muralikrishnan
44a45d44e2 [xrp] Update XRP C++ Method/Class Descriptions for Java Parity (#7351)
Co-authored-by: Ryan Blue <ryanzblue@gmail.com>
2024-11-06 16:12:16 -07:00
Ryan Blue
af652817d9 [commands] Revert "WrappedCommand: Call wrapped command initSendable (#6471)" (#7353)
This reverts commit 7bc0380694.

Calling initSendable on the wrapped command is fundamentally flawed as the wrapper is the command being sent.
2024-11-06 16:10:37 -07:00
Ryan Blue
5a16b0e108 [wpilib] Refactor Alert (#7279)
This refactors Alert in both c++ and java to fix the issues with the current c++ implementation and improve performance.

Currently, constructing an Alert adds it to a list of Alerts with the same group and type. Activating an alert sets a flag on the alert. When the SendableAlerts is polled (GetStrings), the entire list is iterated over, filtered, and the filtered list is sorted by timestamp. This leads to a worst case O(m + nlog(n)) time complexity for GetStrings, where m and n are the count of total constructed alerts active alerts respectively. It also allocates intermediate data structures to hold the active alert strings for sorting.

This changes the implementation to improve the performance of GetStrings, by shifting the sorting overhead to Alert.Set
Constructing the Alert only initializes the alert's initial state, and initializes the SendableAlerts for the group if it is not already initialized.
Activating or deactivating an alert sets an internal flag for state tracking, and inserts or removes a structure containing the timestamp and text into a self-sorting data structure (std::set, TreeSet) containing other active alerts with the same group and type. (worst case O(log(n))
Now, SendableAlerts.GetStrings only has to iterate over the structure and copy the strings to the returned array. (amortized O(n))

This also fixes the c++ implementation by removing the need for SendableAlerts to directly access the Alert.

This also adds a helper method to SendableRegistry to force initialization of the instance to prevent static initialization ordering issues.
2024-11-06 16:09:06 -07:00
sciencewhiz
71c050389a [wpiunits] Restore and deprecate measure negate (#7345)
Delegate to unaryMinus.

This ensures there's a deprecation message to help users with migration, vs just a compile error.
2024-11-06 16:07:31 -07:00
Ryan Blue
83fa422338 [wpiutil] DynamicStruct: Fix decoding of signed integers (#7350)
Add tests for both C++ and Java.
2024-11-05 17:45:49 -07:00
Tyler Veness
3113627be6 [wpimath] Fix PIDController error tolerance getters (#7341) 2024-11-05 09:52:22 -07:00
Thad House
f2d2500d1d [wpiutil] Check MSVC Runtime (#7301) 2024-11-05 09:51:48 -07:00
Tyler Veness
63e623d70b [wpimath] Fix case and order of HolonomicDriveController PID getters (#7342) 2024-11-05 09:50:17 -07:00
Tyler Veness
9e8d37c03b [wpimath] Remove unhelpful test fixtures (#7344)
These test fixtures were adding complexity while only saving one line of
object initialization per test. Our other tests like this just make the
object at the top of each test.
2024-11-05 09:49:38 -07:00
truher
ad09d73dd6 [ntcore] Replace "ValueListenerPoller" with "NetworkTableListenerPoller" in docs (#7328) 2024-11-05 09:49:03 -07:00
Ryan Blue
3fd33b1f72 [wpiutil] DynamicStruct: Clear nested fields when updating/adding a schema (#7334) 2024-11-05 09:48:24 -07:00
Jade
043c155087 Fix a few clangd warnings (#7335) 2024-11-05 09:47:54 -07:00
Ryan Blue
7a6c7af412 [wpiutil] Fix dynamic struct decoding for nested structs (#7346)
After a struct-type field descriptor had offsets calculated more than once, IsBitField would return true, causing the second call to CalculateOffsets to calculate incorrect offsets.
2024-11-05 06:43:04 -07:00
Ryan Blue
8588b5e520 [glass] Revert "Storage: Store Value by value" (#7333)
This reverts commit 309b370223.

Storage::GetChildArray never initializes the child array when the entry is new, so it returns invalid references.
2024-11-04 23:34:18 -07:00
Jade
debb52156c [wpilib] Clamp sim battery voltage to 0 (#7325) 2024-11-03 10:37:44 -08:00
Kavin Muralikrishnan
23e71e10e4 [xrp] Add getter for XRP LED (#7327) 2024-11-03 10:32:03 -08:00
Sam Carlberg
44c0bbc4a9 [epilogue] Generate unique names for variables used in instanceof chains (#7323)
Fixes an issue where the variable names would clash with the field names of the associated VarHandles.
2024-11-02 19:11:22 -07:00
Tyler Veness
a48f3c35f4 Remove argv usage from Python scripts (#7311)
argparse will automatically read sys.argv, so we don't need to pass it
in manually. Furthermore, none of our scripts customize argv.
2024-11-02 19:09:32 -07:00
Tyler Veness
7c91b81906 [ci] Upgrade to wpiformat 2024.45 (#7326) 2024-11-02 17:56:55 -07:00
Jade
eb8583596c [ci] Remove parts of Bazel CI (#7324)
We were building huge amounts with bazel we were already building
otherwise. We've been getting heavily backlogged in CI because of the amount
of CI jobs we are running versus our maximum runners quota (particularly on Mac), so this really isn't worth it right now.
2024-11-02 07:05:24 -07:00
Tyler Veness
dfd1084526 [wpimath] Replace pi with symbol in docs (#7322) 2024-11-01 17:16:18 -07:00
Peter Johnson
2cfe114c78 [wpiutil] DynamicStruct: Store StructDescriptor by value 2024-10-31 22:04:13 -07:00
Peter Johnson
caae5357b7 [wpilibc] Mechanism2d: Store roots by value 2024-10-31 22:04:13 -07:00
Peter Johnson
22e91bfacd [wpilibc] ShuffleboardInstance: Store ShuffleboardTab by value 2024-10-31 22:04:13 -07:00
Peter Johnson
67e1b5fe95 [sim] halsim_ws_core: Store SimDeviceValueData by value 2024-10-31 22:04:13 -07:00
Peter Johnson
27e07d6787 [ntcore] ClientImpl3: Store Entry by value 2024-10-31 22:04:13 -07:00
Peter Johnson
e49d452e46 [glass] Context: Store storageRoots by value 2024-10-31 22:04:13 -07:00
Peter Johnson
5c0edc2410 [glass] Field2D: Store field objects by value 2024-10-31 22:04:13 -07:00
Peter Johnson
309b370223 [glass] Storage: Store Value by value
This is possible because std::map has stable value pointers.
2024-10-31 22:04:13 -07:00
Peter Johnson
f620141e0d [wpiutil] Replace LLVM StringMap impl with std::map
As string_view operations on std::map<std::string> won't be integrated
until C++26, placeholder implementations are used which are less efficient
in a couple of situations (e.g. insert with hint).
2024-10-31 22:04:13 -07:00
Ryan Blue
5f3cf517d3 [commands] C++: Allow CommandPtrs to be owned by the scheduler (#7310)
Previously users would have to keep track of dynamically created CommandPtrs. This adds an ownership-taking version of schedule which places the command in a temporary store in the scheduler. The command will be freed when the command's lifecycle ends.
2024-10-31 22:03:12 -07:00
Tyler Veness
328a781040 [wpimath] Port Rotation2d Java tests to C++ (#7318) 2024-10-31 20:40:14 -07:00
Thad House
ebf83e4340 [ntcoreffi] Add ntcoreffi headers zip (#7229) 2024-10-31 20:39:56 -07:00
Tyler Veness
9f6f267f5c [wpimath] DARE: Use wpi::expected instead of exceptions (#7312) 2024-10-31 20:37:57 -07:00
Tyler Veness
21980c7447 [sysid] Clamp feedback measurement delay to zero or higher (#7319)
LQR latency compensation applies exponential decay to the feedback
gains, so a negative latency causes them to exponentially grow.
2024-10-31 20:37:15 -07:00
David Vo
75fc4d18ef [bazel] Fix test attempting to upload to readonly cache (#7302) 2024-10-31 20:36:58 -07:00
Ryan Blue
8f81b7723d [ci] Disable caching for setup-go (#7320)
setup-go is warning because it can't find go.sum, which we don't have.
2024-10-31 20:36:35 -07:00
Sam Carlberg
fe45265a3a [epilogue] Log elements based on the most specific available logger (#7317)
Instead of only logging based on the declared type, loggers will be smarter and do instanceof checks to determine what logger should be used.

Note that diamond inheritance may cause unexpected behavior for non-logged classes that implement multiple logged interfaces.
2024-10-31 20:34:41 -07:00
Sam Carlberg
0f313c672f [epilogue] Autogenerate nicer data names by default, not just raw element names (#7167)
eg "getFoo()" will now be logged as "Foo", or "m_leftMotor" as "Left Motor"

It is now a compilation error to reuse the same logged name for multiple elements (since whatever is declared last would overwrite anything logged before it)

Do not log record fields (just use the accessors). This also fixes an issue where records could never be logged due to identical member and accessor names

Also skips toString, hashCode, and clone methods when generating loggers
2024-10-31 20:34:00 -07:00
Thad House
aaf139320e [upstream_utils] Fix protobuf GNUC_MINOR macro (#7316) 2024-10-30 22:42:24 -07:00
Ryan Blue
89c5d98fe9 [build] Use pathlib in wpiunits generation script (#7306)
Makes wpiunits more consistent with the other generation scripts.

Also sort imports, remove args from main.
2024-10-29 23:05:48 -07:00
Tyler Veness
defcc02806 [wpimath] Clean up LTV controller includes (#7313) 2024-10-29 23:03:17 -07:00
Ryan Blue
e6e928d670 [ci] Increase keychain timeout to 6 hours (#7314) 2024-10-29 23:02:47 -07:00
Ryan Blue
f03e0cdf6a [wpilib] Add explanation for HID GetBumper/Touchpad deprecations (#7304) 2024-10-28 19:29:42 -07:00
Ryan Blue
412c042c6c Use LF in generated files (#7305) 2024-10-28 19:28:58 -07:00
Ryan Blue
f44c3eda43 [ci] Update actions to python 3.12 (#7308) 2024-10-28 18:09:35 -07:00
Ryan Blue
018dcaea4f [ci] Check return code of subprocesses in pregen_all (#7307)
Previously errors were ignored.

Also makes the script more cross-platform by using the current python executable to run the subprocesses.
2024-10-28 18:07:30 -07:00
Jade
85ffb7814b [build] Update Gradle to 8.10.2 (#7164) 2024-10-28 00:44:30 -07:00
Brendan Raykoff
6207992709 [wpilibj] RobotController: Add Java unit support (#7278) 2024-10-27 23:41:15 -07:00
Tyler Veness
42a433b6fa [sysid] Remove unused includes and inline short functions (#7296) 2024-10-27 23:40:26 -07:00
sciencewhiz
2c857cd82a [ntcore] Use NT3 client identity in front of unique id (#7293)
This way all NT3 clients are not identified as just NT3.
2024-10-26 18:56:04 -07:00
Gold856
1c220ebc60 [build] CMake: add Doxygen doc generation (#7286) 2024-10-25 10:45:53 -07:00
Jade
1cfed736ce [build] CMake: Make Protobuf dependency actually optional (#7291) 2024-10-25 08:52:38 -07:00
Ryan Blue
46b5631ba7 [docs] Fix wpiutil thirdparty include roots in docs (#7288) 2024-10-25 06:52:14 -07:00
Jade
d2b19d8928 [docs] Add WITH_PROTOBUF to README-CMAKE (#7289) 2024-10-25 06:51:35 -07:00
Joseph Eng
9a5f73d787 [cscore] Add back VideoProperty handle member initializer (#7283) 2024-10-23 21:46:20 -07:00
Tyler Veness
db552317e7 [dlt, rtns] Fix libssh deprecation warnings (#7284)
```
/home/tav/frc/wpilib/allwpilib/roborioteamnumbersetter/src/main/native/cpp/SshSession.cpp: In member function ‘void rtns::SshSession::Execute(std::string_view)’:
/home/tav/frc/wpilib/allwpilib/roborioteamnumbersetter/src/main/native/cpp/SshSession.cpp:89:44: warning: ‘int ssh_channel_get_exit_status(ssh_channel)’ is deprecated [-Wdeprecated-declarations]
   89 |   INFO("{} {}", ssh_channel_get_exit_status(channel), cmd);
      |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
```
```
/home/tav/frc/wpilib/allwpilib/roborioteamnumbersetter/src/main/native/cpp/SshSession.cpp:139:44: warning: ‘int ssh_channel_get_exit_status(ssh_channel)’ is deprecated [-Wdeprecated-declarations]
  139 |   INFO("{} {}", ssh_channel_get_exit_status(channel), cmd);
      |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
```
```
/home/tav/frc/wpilib/allwpilib/roborioteamnumbersetter/src/main/native/cpp/SshSession.cpp:143:46: warning: ‘int ssh_channel_get_exit_status(ssh_channel)’ is deprecated [-Wdeprecated-declarations]
  143 |     *exitStatus = ssh_channel_get_exit_status(channel);
      |                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
```
```
/home/tav/frc/wpilib/allwpilib/datalogtool/src/main/native/cpp/Sftp.cpp:79:33: warning: ‘int sftp_async_read_begin(sftp_file, uint32_t)’ is deprecated [-Wdeprecated-declarations]
   79 |   int rv = sftp_async_read_begin(m_handle, len);
      |            ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
```
```
/home/tav/frc/wpilib/allwpilib/datalogtool/src/main/native/cpp/Sftp.cpp: In member function ‘size_t sftp::File::AsyncRead(void*, uint32_t, AsyncId)’:
/home/tav/frc/wpilib/allwpilib/datalogtool/src/main/native/cpp/Sftp.cpp:87:28: warning: ‘int sftp_async_read(sftp_file, void*, uint32_t, uint32_t)’ is deprecated [-Wdeprecated-declarations]
   87 |   auto rv = sftp_async_read(m_handle, data, len, id);
      |             ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
```
2024-10-23 20:34:54 -06:00
Ryan Blue
03cb3c70b4 [epilogue] Set source and target java versions for epilogue-processor tests (#7282) 2024-10-23 17:17:19 -06:00
Nicholas Armstrong
ac907f755a [wpiunits] Add resistance units (#7168) 2024-10-23 08:20:17 -06:00
Nicholas Armstrong
a3b12b3bd9 [wpimath] ChassisSpeeds fromRelative and discretize methods made instance methods (#7115) 2024-10-22 17:23:17 -06:00
Tyler Veness
cbc9264468 [ci] Upgrade to wpiformat 2024.44 (#7256)
This makes the C identifier list scanner correctly handle single quotes
in numeric literals.
2024-10-22 17:19:12 -06:00
Jade
28ac2e3554 Update version in DevelopmentBuilds (#7077) 2024-10-22 09:18:37 -06:00
Jade
58c0cd46b1 [build] Remove cmake toolchains (#7254)
They aren't used and should instead be taken from https://github.com/wpilibsuite/opensdk
2024-10-22 08:36:42 -06:00
Peter Johnson
115a02211c [cscore] HttpCamera: Auto-detect mode from stream if not set (#7248) 2024-10-22 08:35:05 -06:00
Peter Johnson
f553dee6cb [cscore] HttpCamera: Send width/height/fps stream settings (#7247) 2024-10-22 08:33:23 -06:00
Peter Johnson
e8d2d1c39a [wpinet] HttpRequest: Keep params ordered (#7246) 2024-10-22 08:32:41 -06:00
Peter Johnson
7cc7fa1845 [cscore] Fix wakeup on sink destruction (#7245) 2024-10-22 08:02:20 -06:00
Peter Johnson
cbdb4e81f6 [cscore] GetNextFrame: Wake up even if no frames received (#7244) 2024-10-22 08:01:41 -06:00
Tyler Veness
05c7fd929b [wpimath] Make various classes constexpr (#7237) 2024-10-22 07:58:06 -06:00
Tyler Veness
0c824bd447 [wpimath] Take finite difference stencil points by const ref (#7238) 2024-10-21 22:33:56 -07:00
Tyler Veness
ed18b41198 [upstream_utils] Add Eigen geometry module (#7242)
WPICal needs it.
2024-10-21 22:29:35 -07:00
Joseph Eng
6745fc7c2f [wpimath] Fix C++ pose estimator poseEstimate initialization (#7249) 2024-10-21 22:29:04 -07:00
Gold856
40af8db28a [wpilibc] DataLogManager: Rename console log entry to "console" (#7240)
This matches Java.
2024-10-21 06:34:29 -06:00
Tyler Veness
5ac132f6a2 [wpiutil] Make circular buffer classes constexpr (#7232)
The circular buffer class that uses std::vector internally can be used
in a constant expression as long as it doesn't survive to runtime.
2024-10-20 15:33:40 -07:00
Tyler Veness
dd72a78aa4 [upstream_utils] Upgrade to libuv 1.49.2 (#7226) 2024-10-19 09:55:45 -07:00
PJ Reiniger
36e0c9d6db [build] MVP for building with bazel (#6994) 2024-10-19 09:54:49 -07:00
Tyler Veness
95b9bd880b [wpimath] Make geometry classes constexpr (#7222) 2024-10-18 16:08:41 -07:00
Joseph Eng
2054d0f57e [wpimath] Fix pose estimator reset methods (#7225) 2024-10-18 16:06:32 -07:00
Thad House
ee22482f4a [build] ntcoreffi: Don't link to chipobject (#7228) 2024-10-18 16:06:10 -07:00
Étienne Beaulac
796dbd3b86 [wpilib] Add Timer.isRunning() method (#7220) 2024-10-17 17:03:40 -07:00
Gold856
0424e5ba36 [build] Remove unnecessary symbol exclusions (#7221)
The symbol exporter in native-utils was updated and stopped exporting the excluded symbols.
2024-10-17 16:19:19 -07:00
Ryan Heuer
f7dddb8014 [glass] Add Alerts widget (#7219) 2024-10-16 13:45:56 -06:00
Ryan Blue
68715aa484 [wpilibc] SPI & I2C: Use handle wrapper to close port (#7217) 2024-10-16 11:08:44 -06:00
Tyler Veness
fad06ae1e7 Merge .inc files into headers (#7215) 2024-10-15 23:42:57 -07:00
Ryan Heuer
40caabea23 [glass] Align Field2d border and image padding (#7214) 2024-10-15 22:02:08 -07:00
Ryan Blue
59dc9ad8f4 [examples] Rename SysId example to SysIdRoutine (#7213)
CMake target output conflicts with sysid (the application) on windows
2024-10-15 22:01:05 -07:00
Ryan Blue
2b1c5aa4fc [wpilibc] Check for invalid handle in destructors (#7212)
Moved-from objects have invalid handles.
2024-10-15 19:56:13 -07:00
Peter Johnson
0bada2e102 [ntcore] Merge .inc files into headers (#7210) 2024-10-14 22:42:58 -07:00
Tyler Veness
ee281ea448 [wpimath] Merge .inc files into headers (#7209)
Splitting the files didn't help readability or save compilation time and
it confused contributors. Merging them is also in line with how C++
modules will be written.
2024-10-14 16:08:10 -07:00
Peter Johnson
bedfc09268 [ntcore] Add missing multi-subscribe C API functions (#7203)
Also export recently added C API functions.
2024-10-14 09:55:36 -06:00
1120 changed files with 58372 additions and 25773 deletions

20
.bazelignore Normal file
View File

@@ -0,0 +1,20 @@
build_cmake
build-cmake
# Auto generated by vscode
apriltag/bin
cameraserver/bin
cameraserver/multiCameraServer/bin
cscore/bin
fieldImages/bin
hal/bin
developerRobot/bin
ntcore/bin
romiVendordep/bin
wpilibNewCommands/bin
wpilibj/bin
wpimath/bin
wpinet/bin
wpiutil/bin
wpiunits/bin
xrpVendordep/bin

51
.bazelrc Normal file
View File

@@ -0,0 +1,51 @@
try-import %workspace%/bazel_auth.rc
try-import %workspace%/user.bazelrc
common --noenable_bzlmod
build --java_language_version=17
build --java_runtime_version=roboriojdk_17
build --tool_java_language_version=17
build --tool_java_runtime_version=remotejdk_17
test --test_output=errors
test --test_verbose_timeout_warnings
import shared/bazel/compiler_flags/sanitizers.rc
import shared/bazel/compiler_flags/base_linux_flags.rc
import shared/bazel/compiler_flags/linux_flags.rc
import shared/bazel/compiler_flags/osx_flags.rc
import shared/bazel/compiler_flags/roborio_flags.rc
import shared/bazel/compiler_flags/windows_flags.rc
import shared/bazel/compiler_flags/coverage_flags.rc
build:build_java --test_tag_filters=allwpilib-build-java --build_tag_filters=allwpilib-build-java
build:build_cpp --test_tag_filters=+allwpilib-build-cpp --build_tag_filters=+allwpilib-build-cpp
build:no_example --test_tag_filters=-wpi-example --build_tag_filters=-wpi-example
test:no_example --test_tag_filters=-wpi-example --build_tag_filters=-wpi-example
# Build Buddy Cache Setup
build:build_buddy --bes_results_url=https://app.buildbuddy.io/invocation/
build:build_buddy --bes_backend=grpcs://remote.buildbuddy.io
build:build_buddy --remote_cache=grpcs://remote.buildbuddy.io
build:build_buddy --remote_timeout=3600
# Additional suggestions from buildbuddy for speed
build:build_buddy --experimental_remote_cache_compression
build:build_buddy --experimental_remote_cache_compression_threshold=100
build:build_buddy --noslim_profile
build:build_buddy --experimental_profile_include_target_label
build:build_buddy --experimental_profile_include_primary_output
build:build_buddy --nolegacy_important_outputs
common:build_buddy_readonly --noremote_upload_local_results
# This config should be used locally. It downloads more than the CI version
build:remote_user --config=build_buddy
build:remote_user --config=build_buddy_readonly
build:remote_user --remote_download_toplevel
build:ci --config=build_buddy
build:ci --remote_download_minimal
build --build_metadata=REPO_URL=https://github.com/wpilibsuite/allwpilib.git

1
.bazelversion Normal file
View File

@@ -0,0 +1 @@
7.3.1

View File

@@ -2,7 +2,7 @@
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
labels: 'type: bug'
assignees: ''
---

View File

@@ -2,7 +2,7 @@
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
labels: 'type: feature'
assignees: ''
---

View File

@@ -2,7 +2,7 @@
name: Question
about: Ask about features or parts of this project
title: ''
labels: ''
labels: 'type: support'
assignees: ''
---

View File

@@ -0,0 +1,27 @@
name: 'Setup BuildBuddy acache'
description: 'Sets up the build buddy cache to be readonly / writing based on the presence of environment variables'
inputs:
token:
description: 'Build Buddy API token'
runs:
using: "composite"
steps:
- name: Setup without key
env:
API_KEY: ${{ inputs.token }}
if: ${{ env.API_KEY == '' }}
shell: bash
run: |
echo "No API key secret detected, will setup readonly cache"
echo "build:ci --config=build_buddy_readonly" > bazel_auth.rc
- name: Set with key
env:
API_KEY: ${{ inputs.token }}
if: ${{ env.API_KEY != '' }}
shell: bash
run: |
echo "API Key detected!"
echo "build:build_buddy --remote_header=x-buildbuddy-api-key=${{ env.API_KEY }}" > bazel_auth.rc

109
.github/workflows/bazel.yml vendored Normal file
View File

@@ -0,0 +1,109 @@
name: Bazel
on: [pull_request, push]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
build-windows:
strategy:
fail-fast: false
matrix:
include:
- { name: "Windows (native)", os: windows-2022, action: "test", config: "--config=windows", }
- { name: "Windows (arm)", os: windows-2022, action: "build", config: "--config=windows_arm", }
name: "Build ${{ matrix.name }}"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 17
architecture: x64
- id: Setup_build_buddy
uses: ./.github/actions/setup-build-buddy
with:
token: ${{ secrets.BUILDBUDDY_API_KEY }}
- name: Build Release
run: bazel --output_user_root=C:\\bazelroot ${{ matrix.action }} -k ... --config=ci -c opt ${{ matrix.config }} --verbose_failures
shell: bash
build-mac:
name: "Mac"
runs-on: macos-14
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- id: Setup_build_buddy
uses: ./.github/actions/setup-build-buddy
with:
token: ${{ secrets.BUILDBUDDY_API_KEY }}
- name: Build Release
run: bazel test -k ... --config=ci -c opt --config=macos --nojava_header_compilation --verbose_failures
shell: bash
build-linux:
strategy:
fail-fast: false
matrix:
include:
- { name: "Linux (native)", os: ubuntu-22.04, action: "test", config: "--config=linux", }
- { name: "Linux (roborio)", os: ubuntu-22.04, action: "build", config: "--config=roborio", }
name: "${{ matrix.name }}"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: bazelbuild/setup-bazelisk@v3
- id: Setup_build_buddy
uses: ./.github/actions/setup-build-buddy
with:
token: ${{ secrets.BUILDBUDDY_API_KEY }}
- name: Build and Test Release
run: bazel ${{ matrix.action }} ... --config=ci -c opt ${{ matrix.config }} -k --verbose_failures
buildifier:
name: "buildifier"
runs-on: ubuntu-22.04
steps:
- name: Set up Go 1.15.x
uses: actions/setup-go@v5
with:
cache: false
go-version: 1.15.x
id: go
- name: Install Buildifier
run: |
cd $(mktemp -d)
GO111MODULE=on go get github.com/bazelbuild/buildtools/buildifier@6.0.0
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Run buildifier
run: buildifier -warnings all --lint=fix -r .
- name: Check Output
run: git --no-pager diff --exit-code HEAD
- name: Generate diff
run: git diff HEAD > bazel-lint-fixes.patch
if: ${{ failure() }}
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.platform }}-bazel-lint-fixes
path: bazel-lint-fixes.patch
if: ${{ failure() }}

51
.github/workflows/cmake-android.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: CMake Android
on: [pull_request, push]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
env:
SCCACHE_WEBDAV_ENDPOINT: "https://frcmaven.wpi.edu/artifactory/wpilib-generic-cache-cmake-local"
SCCACHE_WEBDAV_KEY_PREFIX: "sccache"
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
name: Android Arm64
abi: arm64-v8a
- os: ubuntu-22.04
name: Android X64
abi: "x86_64"
name: "Build - ${{ matrix.name }}"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: r27c
add-to-path: false
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.5
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install -y ninja-build
- name: configure
run: cmake --preset with-sccache -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_WPILIB=OFF -DWITH_GUI=OFF -DWITH_CSCORE=OFF -DWITH_TESTS=OFF -DWITH_SIMULATION_MODULES=OFF -DWITH_PROTOBUF=OFF -DWITH_JAVA=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_TOOLCHAIN_FILE=${{ steps.setup-ndk.outputs.ndk-path }}/build/cmake/android.toolchain.cmake -DANDROID_ABI="${{ matrix.abi }}" -DANDROID_PLATFORM=android-24
- name: build
run: cmake --build build-cmake --parallel $(nproc)

View File

@@ -33,17 +33,17 @@ jobs:
env:
GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}"
NUMBER: ${{ github.event.issue.number }}
- name: Set up Python 3.10
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.10'
python-version: '3.12'
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Install wpiformat
run: pip3 install wpiformat==2024.42
run: pip3 install wpiformat==2024.45
- name: Run wpiformat
run: wpiformat
- name: Run spotlessApply
@@ -81,10 +81,10 @@ jobs:
env:
GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}"
NUMBER: ${{ github.event.issue.number }}
- name: Set up Python 3.9
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: 3.9
python-version: '3.12'
- name: Install jinja
run: python -m pip install jinja2
- name: Install protobuf dependencies

View File

@@ -121,7 +121,7 @@ jobs:
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
- name: Set Keychain Lock Timeout
run: security set-keychain-settings -lut 3600
run: security set-keychain-settings -lut 21600
if: |
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))

View File

@@ -22,12 +22,12 @@ jobs:
run: |
git checkout -b pr
git branch -f main origin/main
- name: Set up Python 3.10
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.10'
python-version: '3.12'
- name: Install wpiformat
run: pip3 install wpiformat==2024.42
run: pip3 install wpiformat==2024.45
- name: Run
run: wpiformat
- name: Check output
@@ -61,12 +61,12 @@ jobs:
git config --global --add safe.directory /__w/allwpilib/allwpilib
git checkout -b pr
git branch -f main origin/main
- name: Set up Python 3.10
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.10'
python-version: '3.12'
- name: Install wpiformat
run: pip3 install wpiformat==2024.42
run: pip3 install wpiformat==2024.45
- name: Create compile_commands.json
run: |
./gradlew generateCompileCommands -Ptoolchain-optional-roboRio

View File

@@ -6,7 +6,7 @@ import sys
from pathlib import Path
def main(argv):
def main():
script_path = Path(__file__).resolve()
REPO_ROOT = script_path.parent.parent.parent
parser = argparse.ArgumentParser()
@@ -15,26 +15,64 @@ def main(argv):
help="Path to the quickbuf protoc plugin",
required=True,
)
args = parser.parse_args(argv)
subprocess.run(["python", f"{REPO_ROOT}/hal/generate_usage_reporting.py"])
subprocess.run(["python", f"{REPO_ROOT}/ntcore/generate_topics.py"])
subprocess.run(["python", f"{REPO_ROOT}/wpimath/generate_numbers.py"])
args = parser.parse_args()
subprocess.run(
[sys.executable, f"{REPO_ROOT}/hal/generate_usage_reporting.py"], check=True
)
subprocess.run(
[sys.executable, f"{REPO_ROOT}/ntcore/generate_topics.py"], check=True
)
subprocess.run(
[sys.executable, f"{REPO_ROOT}/wpimath/generate_numbers.py"], check=True
)
subprocess.run(
[
"python",
sys.executable,
f"{REPO_ROOT}/wpimath/generate_quickbuf.py",
f"--quickbuf_plugin={args.quickbuf_plugin}",
]
],
check=True,
)
subprocess.run(["python", f"{REPO_ROOT}/wpiunits/generate_units.py"])
subprocess.run(["python", f"{REPO_ROOT}/wpilibc/generate_hids.py"])
subprocess.run(["python", f"{REPO_ROOT}/wpilibj/generate_hids.py"])
subprocess.run(["python", f"{REPO_ROOT}/wpilibNewCommands/generate_hids.py"])
subprocess.run(["python", f"{REPO_ROOT}/wpilibc/generate_pwm_motor_controllers.py"])
subprocess.run(["python", f"{REPO_ROOT}/wpilibj/generate_pwm_motor_controllers.py"])
subprocess.run(["python", f"{REPO_ROOT}/thirdparty/imgui_suite/generate_gl3w.py"])
subprocess.run(f"{REPO_ROOT}/thirdparty/imgui_suite/generate_fonts.sh")
subprocess.run(
[
sys.executable,
f"{REPO_ROOT}/wpimath/generate_nanopb.py",
],
check=True,
)
subprocess.run(
[sys.executable, f"{REPO_ROOT}/wpiunits/generate_units.py"], check=True
)
subprocess.run(
[sys.executable, f"{REPO_ROOT}/wpilibc/generate_hids.py"], check=True
)
subprocess.run(
[sys.executable, f"{REPO_ROOT}/wpilibj/generate_hids.py"], check=True
)
subprocess.run(
[sys.executable, f"{REPO_ROOT}/wpilibNewCommands/generate_hids.py"], check=True
)
subprocess.run(
[sys.executable, f"{REPO_ROOT}/wpilibc/generate_pwm_motor_controllers.py"],
check=True,
)
subprocess.run(
[sys.executable, f"{REPO_ROOT}/wpilibj/generate_pwm_motor_controllers.py"],
check=True,
)
subprocess.run(
[
sys.executable,
f"{REPO_ROOT}/wpiutil/generate_nanopb.py",
],
check=True,
)
subprocess.run(
[sys.executable, f"{REPO_ROOT}/thirdparty/imgui_suite/generate_gl3w.py"],
check=True,
)
subprocess.run(f"{REPO_ROOT}/thirdparty/imgui_suite/generate_fonts.sh", check=True)
if __name__ == "__main__":
main(sys.argv[1:])
main()

View File

@@ -18,12 +18,12 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python 3.9
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Install jinja
run: python -m pip install jinja2
python-version: '3.12'
- name: Install jinja and protobuf
run: python -m pip install jinja2 protobuf grpcio-tools
- 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.3/protoc-gen-quickbuf-1.3.3-linux-x86_64.exe && chmod +x protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
- name: Regenerate all

View File

@@ -119,7 +119,7 @@ jobs:
if: |
matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' && github.ref == 'refs/heads/main')
- name: Set Keychain Lock Timeout
run: security set-keychain-settings -lut 3600
run: security set-keychain-settings -lut 21600
if: |
matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' && github.ref == 'refs/heads/main')
- name: Set Java Heap Size

View File

@@ -22,10 +22,10 @@ jobs:
run: |
git checkout -b pr
git branch -f main origin/main
- name: Set up Python 3.9
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: 3.9
python-version: '3.12'
- name: Configure committer identity
run: |
git config --global user.email "you@example.com"
@@ -35,102 +35,122 @@ jobs:
cd upstream_utils
./apriltag.py clone
./apriltag.py copy-src
./apriltag.py format-patch
- name: Run argparse_lib.py
run: |
cd upstream_utils
./argparse_lib.py clone
./argparse_lib.py copy-src
./argparse_lib.py format-patch
- name: Run eigen.py
run: |
cd upstream_utils
./eigen.py clone
./eigen.py copy-src
./eigen.py format-patch
- name: Run expected.py
run: |
cd upstream_utils
./expected.py clone
./expected.py copy-src
./expected.py format-patch
- name: Run fmt.py
run: |
cd upstream_utils
./fmt.py clone
./fmt.py copy-src
./fmt.py format-patch
- name: Run gcem.py
run: |
cd upstream_utils
./gcem.py clone
./gcem.py copy-src
./gcem.py format-patch
- name: Run gl3w.py
run: |
cd upstream_utils
./gl3w.py clone
./gl3w.py copy-src
./gl3w.py format-patch
- name: Run glfw.py
run: |
cd upstream_utils
./glfw.py clone
./glfw.py copy-src
./glfw.py format-patch
- name: Run googletest.py
run: |
cd upstream_utils
./googletest.py clone
./googletest.py copy-src
./googletest.py format-patch
- name: Run imgui.py
run: |
cd upstream_utils
./imgui.py clone
./imgui.py copy-src
./imgui.py format-patch
- name: Run implot.py
run: |
cd upstream_utils
./implot.py clone
./implot.py copy-src
./implot.py format-patch
- name: Run json.py
run: |
cd upstream_utils
./json.py clone
./json.py copy-src
./json.py format-patch
- name: Run libuv.py
run: |
cd upstream_utils
./libuv.py clone
./libuv.py copy-src
./libuv.py format-patch
- name: Run llvm.py
run: |
cd upstream_utils
./llvm.py clone
./llvm.py copy-src
./llvm.py format-patch
- name: Run mpack.py
run: |
cd upstream_utils
./mpack.py clone
./mpack.py copy-src
./mpack.py format-patch
- name: Run stack_walker.py
run: |
cd upstream_utils
./stack_walker.py clone
./stack_walker.py copy-src
./stack_walker.py format-patch
- name: Run memory.py
run: |
cd upstream_utils
./memory.py clone
./memory.py copy-src
./memory.py format-patch
- name: Run protobuf.py
run: |
cd upstream_utils
./protobuf.py clone
./protobuf.py copy-src
./protobuf.py format-patch
- name: Run sleipnir.py
run: |
cd upstream_utils
./sleipnir.py clone
./sleipnir.py copy-src
./sleipnir.py format-patch
- name: Run stb.py
run: |
cd upstream_utils
./stb.py clone
./stb.py copy-src
./stb.py format-patch
- name: Add untracked files to index so they count as changes
run: git add -A
- name: Check output
run: git --no-pager diff --exit-code HEAD
run: git --no-pager diff --exit-code HEAD ':!*.bazel'

6
.gitignore vendored
View File

@@ -15,6 +15,8 @@ networktables.json
ntcore/connectionlistenertest.json
ntcore/timesynctest.json
nanopb_pb2.py
# Created by the jenkins test script
test-reports
@@ -249,9 +251,7 @@ imgui.ini
/bazel-*
user.bazelrc
coverage_report/
bazel_auth.rc
# ctest
/Testing/
# protobuf
!wpiprotoplugin.jar

View File

@@ -66,6 +66,14 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
option(BUILD_SHARED_LIBS "Build with shared libs (needed for JNI)" ON)
option(WITH_JAVA "Include Java and JNI in the build" OFF)
option(WITH_JAVA_SOURCE "Build Java source jars" ${WITH_JAVA})
option(WITH_DOCS "Build Doxygen docs (needs Git for versioning)" OFF)
cmake_dependent_option(
DOCS_WARNINGS_AS_ERRORS
"Make docs warnings into errors"
OFF
WITH_DOCS
OFF
)
option(WITH_CSCORE "Build cscore (needs OpenCV)" ON)
option(WITH_NTCORE "Build ntcore" ON)
option(WITH_WPIMATH "Build wpimath" ON)
@@ -123,12 +131,17 @@ set(java_lib_dest java)
if(WITH_JAVA OR WITH_JAVA_SOURCE)
set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
find_package(Java REQUIRED COMPONENTS Development)
find_package(JNI REQUIRED COMPONENTS JVM)
else()
# Protoc requires the java runtime
find_package(Java REQUIRED COMPONENTS Runtime)
if(NOT ANDROID)
find_package(JNI REQUIRED COMPONENTS JVM)
endif()
endif()
if(WITH_DOCS)
find_package(Doxygen REQUIRED)
find_package(Git REQUIRED)
include(AddDoxygenDocs)
add_doxygen_docs()
endif()
find_package(LIBSSH CONFIG 0.7.1)
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
@@ -279,8 +292,6 @@ if(WITH_NTCORE)
add_subdirectory(ntcore)
endif()
add_subdirectory(protoplugin)
if(WITH_WPIMATH)
if(WITH_JAVA)
set(WPIUNITS_DEP_REPLACE ${WPIUNITS_DEP_REPLACE_IMPL})

View File

@@ -13,7 +13,7 @@ This article contains instructions on building projects using a development buil
Development builds are the per-commit build hosted every time a commit is pushed to the [allwpilib](https://github.com/wpilibsuite/allwpilib/) repository. These builds are then hosted on [artifactory](https://frcmaven.wpi.edu/artifactory/webapp/#/home).
To build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version. It is also necessary to use a 2024 GradleRIO version, ie `2024.0.0-alpha-1`
To build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version. It is also necessary to use a 2025 GradleRIO version, ie `2025.1.1-beta-1`
```groovy
wpi.maven.useLocal = false
@@ -28,13 +28,13 @@ Java
```groovy
plugins {
id "java"
id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
}
wpi.maven.useLocal = false
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = '2024.+'
wpi.versions.wpimathVersion = '2024.+'
wpi.versions.wpilibVersion = '2025.+'
wpi.versions.wpimathVersion = '2025.+'
```
C++
@@ -42,13 +42,13 @@ C++
plugins {
id "cpp"
id "google-test-test-suite"
id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
}
wpi.maven.useLocal = false
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = '2024.+'
wpi.versions.wpimathVersion = '2024.+'
wpi.versions.wpilibVersion = '2025.+'
wpi.versions.wpimathVersion = '2025.+'
```
### Development Build Documentation
@@ -64,7 +64,7 @@ Java
```groovy
plugins {
id "java"
id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
}
wpi.maven.useLocal = false
@@ -78,7 +78,7 @@ C++
plugins {
id "cpp"
id "google-test-test-suite"
id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
}
wpi.maven.useLocal = false

25
README-Bazel.md Normal file
View File

@@ -0,0 +1,25 @@
# WPILib Bazel Support
WPILib is normally built with Gradle, but [Bazel](https://www.bazel.build/) can also be used to increase development speed due to the superior caching ability and the ability to use remote caching and remote execution (on select platforms)
## Prerequisites
- Install [Bazelisk](https://github.com/bazelbuild/bazelisk/releases) and add it to your path. Bazelisk is a wrapper that will download the correct version of bazel specified in the repository. Note: You can alias/rename the binary to `bazel` if you want to keep the familiar `bazel build` vs `bazelisk build` syntax.
## Building
To build the entire repository, simply run `bazel build //...`. To run all of the unit tests, run `bazel test //...`
Other examples:
- `bazel build //wpimath/...` - Builds every target in the wpimath folder
- `bazel test //wpiutil:wpiutil-cpp-test` - Runs only the cpp test target in the wpiutil folder
- `bazel coverage //wpiutil/...` - (*Nix only) - Runs a code coverage report for both C++ and Java on all the targets under wpiutil
## User settings
When invoking bazel, it will check if `user.bazelrc` exists for additional, user specified flags. You can use these settings to do things like always ignore buildin a specific folder, or limiting the CPU/RAM usage during a build.
Examples:
- `build --build_tag_filters=-wpi-example` - Do not build any targets tagged with `wpi-example` (Currently all of the targets in wpilibcExamples and wpilibjExamples contain this tag)
- `build -c opt` - Always build optimized targets. The default compiler flags were chosen to build as fast as possible, and thus don't contain many optimizations
- `build -k` - `-k` is analogous to the MAKE flag `--keep-going`, so the build will not stop on the first error.
- ```
build --local_ram_resources=HOST_RAM*.5 # Don't use more than half my RAM when building
build --local_cpu_resources=HOST_CPUS-1 # Leave one core alone
```

View File

@@ -66,6 +66,8 @@ The following build options are available:
* This option will build the HAL and wpilibc/j during the build. The HAL is the simulation HAL, unless the external HAL options are used. The CMake build has no capability to build for the roboRIO.
* `WITH_WPIMATH` (ON Default)
* This option will build the wpimath library. This option must be on to build wpilib.
* `WITH_PROTOBUF` (ON Default)
* This option will build with the protobuf library.
* `WITH_WPIUNITS` (`WITH_JAVA` Default)
* This option will build the wpiunits library. This option must be on to build the Java wpimath library and requires `WITH_JAVA` to also be on.
* `OPENCV_JAVA_INSTALL_DIR`

View File

@@ -17,6 +17,7 @@ Welcome to the WPILib project. This repository contains the HAL, WPILibJ, and WP
- [Custom toolchain location](#custom-toolchain-location)
- [Formatting/Linting](#formattinglinting)
- [CMake](#cmake)
- [Bazel](#bazel)
- [Running examples in simulation](#running-examples-in-simulation)
- [Publishing](#publishing)
- [Structure and Organization](#structure-and-organization)
@@ -149,7 +150,11 @@ Several files within WPILib are generated using Jinja. If a PR is opened that mo
### CMake
CMake is also supported for building. See [README-CMAKE.md](README-CMAKE.md).
CMake is also supported for building. See [README-CMake.md](README-CMake.md).
### Bazel
Bazel is also supported for building. See [README-Bazel.md](README-Bazel.md).
## Running examples in simulation

100
WORKSPACE Normal file
View File

@@ -0,0 +1,100 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Download Extra java rules
http_archive(
name = "rules_jvm_external",
sha256 = "08ea921df02ffe9924123b0686dc04fd0ff875710bfadb7ad42badb931b0fd50",
strip_prefix = "rules_jvm_external-6.1",
url = "https://github.com/bazelbuild/rules_jvm_external/releases/download/6.1/rules_jvm_external-6.1.tar.gz",
)
load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps")
rules_jvm_external_deps()
load("@rules_jvm_external//:defs.bzl", "maven_install")
maven_artifacts = [
"org.ejml:ejml-simple:0.43.1",
"com.fasterxml.jackson.core:jackson-annotations:2.15.2",
"com.fasterxml.jackson.core:jackson-core:2.15.2",
"com.fasterxml.jackson.core:jackson-databind:2.15.2",
"us.hebi.quickbuf:quickbuf-runtime:1.3.3",
"com.google.code.gson:gson:2.10.1",
]
maven_install(
name = "maven",
artifacts = maven_artifacts,
repositories = [
"https://repo1.maven.org/maven2",
"https://frcmaven.wpi.edu/artifactory/release/",
],
)
# Download toolchains
http_archive(
name = "rules_bzlmodrio_toolchains",
sha256 = "2ef1cafce7f4fd4e909bb5de8b0dc771a934646afd55d5f100ff31f6b500df98",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_toolchains/releases/download/2024-1.bcr1/rules_bzlmodRio_toolchains-2024-1.bcr1.tar.gz",
)
load("@rules_bzlmodrio_toolchains//:maven_deps.bzl", "setup_legacy_setup_toolchains_dependencies")
setup_legacy_setup_toolchains_dependencies()
load("@rules_bzlmodrio_toolchains//toolchains:load_toolchains.bzl", "load_toolchains")
load_toolchains()
#
http_archive(
name = "rules_bzlmodrio_jdk",
sha256 = "a00d5fa971fbcad8a17b1968cdc5350688397035e90b0cb94e040d375ecd97b4",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_jdk/releases/download/17.0.8.1-1/rules_bzlmodRio_jdk-17.0.8.1-1.tar.gz",
)
load("@rules_bzlmodrio_jdk//:maven_deps.bzl", "setup_legacy_setup_jdk_dependencies")
setup_legacy_setup_jdk_dependencies()
register_toolchains(
"@local_roborio//:macos",
"@local_roborio//:linux",
"@local_roborio//:windows",
"@local_raspi_32//:macos",
"@local_raspi_32//:linux",
"@local_raspi_32//:windows",
"@local_bullseye_32//:macos",
"@local_bullseye_32//:linux",
"@local_bullseye_32//:windows",
"@local_bullseye_64//:macos",
"@local_bullseye_64//:linux",
"@local_bullseye_64//:windows",
)
setup_legacy_setup_jdk_dependencies()
http_archive(
name = "bzlmodrio-ni",
sha256 = "197fceac88bf44fb8427d5e000b0083118d3346172dd2ad31eccf83a5e61b3ce",
url = "https://github.com/wpilibsuite/bzlmodRio-ni/releases/download/2025.0.0/bzlmodRio-ni-2025.0.0.tar.gz",
)
load("@bzlmodrio-ni//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_ni_cpp_dependencies")
setup_legacy_bzlmodrio_ni_cpp_dependencies()
http_archive(
name = "bzlmodrio-opencv",
sha256 = "5314cce05b49451a46bf3e3140fc401342e53d5f3357612ed4473e59bb616cba",
url = "https://github.com/wpilibsuite/bzlmodRio-opencv/releases/download/2024.4.8.0-4.bcr1/bzlmodRio-opencv-2024.4.8.0-4.bcr1.tar.gz",
)
load("@bzlmodrio-opencv//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_opencv_cpp_dependencies")
setup_legacy_bzlmodrio_opencv_cpp_dependencies()
load("@bzlmodrio-opencv//:maven_java_deps.bzl", "setup_legacy_bzlmodrio_opencv_java_dependencies")
setup_legacy_bzlmodrio_opencv_java_dependencies()

View File

@@ -297,7 +297,7 @@ public class AprilTagDetector implements AutoCloseable {
* @return Results (array of AprilTagDetection)
*/
public AprilTagDetection[] detect(Mat img) {
return AprilTagJNI.detect(m_native, img.cols(), img.rows(), img.cols(), img.dataAddr());
return AprilTagJNI.detect(m_native, img.cols(), img.rows(), (int) img.step1(), img.dataAddr());
}
private long m_native;

View File

@@ -130,7 +130,7 @@ void AprilTagDetector::RemoveFamily(std::string_view fam) {
apriltag_detector_remove_family(
static_cast<apriltag_detector_t*>(m_impl),
static_cast<apriltag_family_t*>(it->second));
DestroyFamily(it->getKey(), it->second);
DestroyFamily(it->first, it->second);
m_families.erase(it);
}
}
@@ -158,7 +158,7 @@ void AprilTagDetector::Destroy() {
void AprilTagDetector::DestroyFamilies() {
for (auto&& entry : m_families) {
DestroyFamily(entry.getKey(), entry.second);
DestroyFamily(entry.first, entry.second);
}
}

View File

@@ -131,6 +131,34 @@ class AprilTagDetectorTest {
return image;
}
@Test
void testDecodeCropped() {
detector.addFamily("tag16h5");
detector.addFamily("tag36h11");
Mat image;
try {
image = loadImage("tag1_640_480.jpg");
} catch (IOException ex) {
fail(ex);
return;
}
// Pre-knowledge -- the tag is within this ROI of this particular test image
var cropped = image.submat(100, 400, 220, 570);
try {
AprilTagDetection[] results = detector.detect(cropped);
assertEquals(1, results.length);
assertEquals("tag36h11", results[0].getFamily());
assertEquals(1, results[0].getId());
assertEquals(0, results[0].getHamming());
} finally {
cropped.release();
image.release();
}
}
@Test
void testDecodeAndPose() {
detector.addFamily("tag16h5");

View File

@@ -21,7 +21,6 @@ plugins {
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'com.diffplug.spotless' version '6.20.0' apply false
id 'com.github.spotbugs' version '6.0.2' apply false
id 'com.google.protobuf' version '0.9.3' apply false
}
wpilibVersioning.buildServerMode = project.hasProperty('buildServer')
@@ -171,5 +170,5 @@ ext.getCurrentArch = {
}
wrapper {
gradleVersion = '8.5'
gradleVersion = '8.11'
}

32
cameraserver/BUILD.bazel Normal file
View File

@@ -0,0 +1,32 @@
load("@rules_cc//cc:defs.bzl", "cc_binary")
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
java_library(
name = "cameraserver-java",
srcs = glob(["src/main/java/**/*.java"]),
visibility = ["//visibility:public"],
deps = [
"//cscore:cscore-java",
"//hal:hal-java",
"//ntcore:networktables-java",
"//wpimath:wpimath-java",
"//wpinet:wpinet-java",
"//wpiutil:wpiutil-java",
"@bzlmodrio-opencv//libraries/java/opencv",
],
)
cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
deps = [
],
)
java_binary(
name = "DevMain-Java",
srcs = ["src/dev/java/edu/wpi/first/cameraserver/DevMain.java"],
main_class = "edu.wpi.first.cameraserver.DevMain",
deps = [
],
)

View File

@@ -30,19 +30,6 @@ apply from: "${rootDir}/shared/opencv.gradle"
nativeUtils.exportsConfigs {
cameraserver {
x64ExcludeSymbols = [
'_CT??_R0?AV_System_error',
'_CT??_R0?AVexception',
'_CT??_R0?AVfailure',
'_CT??_R0?AVruntime_error',
'_CT??_R0?AVsystem_error',
'_CTA5?AVfailure',
'_TI5?AVfailure',
'_CT??_R0?AVout_of_range',
'_CTA3?AVout_of_range',
'_TI3?AVout_of_range',
'_CT??_R0?AVbad_cast'
]
}
}

View File

@@ -0,0 +1,16 @@
load("@rules_java//java:defs.bzl", "java_binary")
java_binary(
name = "multiCameraServer-java",
srcs = ["src/main/java/edu/wpi/Main.java"],
main_class = "edu.wpi.Main",
deps = [
"//cameraserver:cameraserver-java",
"//cscore:cscore-java",
"//hal:hal-java",
"//ntcore:networktables-java",
"//wpimath:wpimath-java",
"//wpiutil:wpiutil-java",
"@maven//:com_google_code_gson_gson",
],
)

View File

@@ -22,7 +22,7 @@ class DefaultCameraServerShared : public frc::CameraServerShared {
void ReportDriverStationErrorV(fmt::string_view format,
fmt::format_args args) override {}
std::pair<std::thread::id, bool> GetRobotMainThreadId() const override {
return std::make_pair(std::thread::id(), false);
return std::pair{std::thread::id(), false};
}
};
} // namespace

View File

@@ -9,10 +9,10 @@
#include <span>
#include <string>
#include <string_view>
#include <vector>
#include <wpi/deprecated.h>
#include "cscore.h"
#include "cscore_cv.h"
namespace frc {
@@ -130,7 +130,9 @@ class CameraServer {
*/
template <typename T>
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
static cs::AxisCamera AddAxisCamera(std::initializer_list<T> hosts);
static cs::AxisCamera AddAxisCamera(std::initializer_list<T> hosts) {
return AddAxisCamera("Axis Camera", hosts);
}
/**
* Adds an Axis IP camera.
@@ -185,7 +187,14 @@ class CameraServer {
template <typename T>
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
static cs::AxisCamera AddAxisCamera(std::string_view name,
std::initializer_list<T> hosts);
std::initializer_list<T> hosts) {
std::vector<std::string> vec;
vec.reserve(hosts.size());
for (const auto& host : hosts) {
vec.emplace_back(host);
}
return AddAxisCamera(name, vec);
}
WPI_UNIGNORE_DEPRECATED
/**
@@ -316,5 +325,3 @@ class CameraServer {
};
} // namespace frc
#include "cameraserver/CameraServer.inc"

View File

@@ -1,33 +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 <string>
#include <vector>
#include "cameraserver/CameraServer.h"
namespace frc {
WPI_IGNORE_DEPRECATED
template <typename T>
inline cs::AxisCamera CameraServer::AddAxisCamera(
std::initializer_list<T> hosts) {
return AddAxisCamera("Axis Camera", hosts);
}
template <typename T>
inline cs::AxisCamera CameraServer::AddAxisCamera(
std::string_view name, std::initializer_list<T> hosts) {
std::vector<std::string> vec;
vec.reserve(hosts.size());
for (const auto& host : hosts) {
vec.emplace_back(host);
}
return AddAxisCamera(name, vec);
}
WPI_UNIGNORE_DEPRECATED
} // namespace frc

View File

@@ -8,7 +8,6 @@
#include <functional>
#include <memory>
#include "cscore.h"
#include "cscore_cv.h"
#include "vision/VisionPipeline.h"
@@ -81,17 +80,35 @@ class VisionRunnerBase {
template <typename T>
class VisionRunner : public VisionRunnerBase {
public:
/**
* Creates a new vision runner. It will take images from the {@code
* videoSource}, send them to the {@code pipeline}, and call the {@code
* listener} when the pipeline has finished to alert user code when it is safe
* to access the pipeline's outputs.
*
* @param videoSource The video source to use to supply images for the
* pipeline
* @param pipeline The vision pipeline to run
* @param listener A function to call after the pipeline has finished
* running
*/
VisionRunner(cs::VideoSource videoSource, T* pipeline,
std::function<void(T&)> listener);
std::function<void(T&)> listener)
: VisionRunnerBase(videoSource),
m_pipeline(pipeline),
m_listener(listener) {}
virtual ~VisionRunner() = default;
protected:
void DoProcess(cv::Mat& image) override;
void DoProcess(cv::Mat& image) override {
m_pipeline->Process(image);
m_listener(*m_pipeline);
}
private:
T* m_pipeline;
std::function<void(T&)> m_listener;
};
} // namespace frc
#include "VisionRunner.inc"
} // namespace frc

View File

@@ -1,36 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <functional>
#include "vision/VisionRunner.h"
namespace frc {
/**
* Creates a new vision runner. It will take images from the {@code
* videoSource}, send them to the {@code pipeline}, and call the {@code
* listener} when the pipeline has finished to alert user code when it is safe
* to access the pipeline's outputs.
*
* @param videoSource The video source to use to supply images for the pipeline
* @param pipeline The vision pipeline to run
* @param listener A function to call after the pipeline has finished running
*/
template <typename T>
VisionRunner<T>::VisionRunner(cs::VideoSource videoSource, T* pipeline,
std::function<void(T&)> listener)
: VisionRunnerBase(videoSource),
m_pipeline(pipeline),
m_listener(listener) {}
template <typename T>
void VisionRunner<T>::DoProcess(cv::Mat& image) {
m_pipeline->Process(image);
m_listener(*m_pipeline);
}
} // namespace frc

View File

@@ -0,0 +1,144 @@
macro(add_doxygen_docs)
set(dirs
apriltag
cameraserver
cscore
fieldImages
hal
ntcore
romiVendordep
wpilibc
wpilibNewCommands
wpimath
wpinet
wpiutil
xrpVendordep
)
foreach(dir ${dirs})
list(APPEND docs_dirs ${dir}/src/main/native/include)
file(GLOB dirs ${dir}/src/main/native/thirdparty/*/include)
list(FILTER dirs EXCLUDE REGEX eigen|protobuf)
set(DOXYGEN_EXCLUDE_PATTERNS "*.pb.h" "**/.clang-tidy" "**/.clang-format")
if(DOCS_WARNINGS_AS_ERRORS)
set(DOXYGEN_WARN_AS_ERROR "FAIL_ON_WARNINGS_PRINT")
list(FILTER dirs EXCLUDE REGEX fmt|memory|units)
list(
APPEND
DOXYGEN_EXCLUDE_PATTERNS
# apriltag
"apriltag_pose.h"
# llvm
"wpi/AlignOf.h"
"wpi/Casting.h"
"wpi/Chrono.h"
"wpi/Compiler.h"
"wpi/ConvertUTF.h"
"wpi/DenseMap.h"
"wpi/DenseMapInfo.h"
"wpi/Endian.h"
"wpi/EpochTracker.h"
"wpi/Errc.h"
"wpi/Errno.h"
"wpi/ErrorHandling.h"
"wpi/bit.h"
"wpi/fs.h"
"wpi/FunctionExtras.h"
"wpi/function_ref.h"
"wpi/Hashing.h"
"wpi/iterator.h"
"wpi/iterator_range.h"
"wpi/ManagedStatic.h"
"wpi/MapVector.h"
"wpi/MathExtras.h"
"wpi/MemAlloc.h"
"wpi/PointerIntPair.h"
"wpi/PointerLikeTypeTraits.h"
"wpi/PointerUnion.h"
"wpi/raw_os_ostream.h"
"wpi/raw_ostream.h"
"wpi/SmallPtrSet.h"
"wpi/SmallSet.h"
"wpi/SmallString.h"
"wpi/SmallVector.h"
"wpi/StringExtras.h"
"wpi/StringMap.h"
"wpi/SwapByteOrder.h"
"wpi/type_traits.h"
"wpi/VersionTuple.h"
"wpi/WindowsError.h"
# libuv
"uv.h"
"uv/**"
# json
"wpi/adl_serializer.h"
"wpi/byte_container_with_subtype.h"
"wpi/json.h"
"wpi/json_fwd.h"
"wpi/ordered_map.h"
# mpack
"wpi/mpack.h"
)
endif()
list(APPEND docs_dirs ${dirs})
list(APPEND docs_dirs ${dir}/src/generated/main/native/include)
endforeach()
set(DOXYGEN_CASE_SENSE_NAMES false)
set(DOXYGEN_EXTENSION_MAPPING inc=C++ no_extension=C++)
set(DOXYGEN_EXTRACT_ALL true)
set(DOXYGEN_EXTRACT_STATIC true)
set(DOXYGEN_FILE_PATTERNS "*")
set(DOXYGEN_FULL_PATH_NAMES true)
set(DOXYGEN_FULL_SIDEBAR false)
set(DOXYGEN_GENERATE_HTML true)
set(DOXYGEN_GENERATE_LATEX false)
set(DOXYGEN_GENERATE_TREEVIEW true)
set(DOXYGEN_HTML_COLORSTYLE "LIGHT")
set(DOXYGEN_HTML_EXTRA_STYLESHEET docs/theme.css)
set(DOXYGEN_JAVADOC_AUTOBRIEF true)
set(DOXYGEN_ALIASES
"effects=\\par <i>Effects:</i>^^"
"notes=\\par <i>Notes:</i>^^"
"requires=\\par <i>Requires:</i>^^"
"requiredbe=\\par <i>Required Behavior:</i>^^"
"concept{2}=<a href=\"md_doc_concepts.html#1\">2</a>"
"defaultbe=\\par <i>Default Behavior:</i>^^"
)
set(DOXYGEN_PROJECT_NAME WPILibC++)
set(DOXYGEN_PROJECT_NUMBER version)
set(DOXYGEN_PROJECT_LOGO wpiutil/src/main/native/resources/wpilib-128.png)
set(DOXYGEN_QUIET true)
set(DOXYGEN_RECURSIVE true)
set(DOXYGEN_STRIP_CODE_COMMENTS false)
set(DOXYGEN_STRIP_FROM_PATH ${docs_dirs})
set(DOXYGEN_STRIP_FROM_INC_PATH ${docs_dirs})
set(DOXYGEN_TIMESTAMP "DATETIME")
set(DOXYGEN_USE_MATHJAX true)
set(DOXYGEN_WARNINGS false)
set(DOXYGEN_WARN_IF_INCOMPLETE_DOC true)
set(DOXYGEN_WARN_IF_UNDOCUMENTED false)
set(DOXYGEN_WARN_NO_PARAMDOC true)
set(DOXYGEN_ENABLE_PREPROCESSING true)
set(DOXYGEN_MACRO_EXPANSION true)
set(DOXYGEN_EXPAND_ONLY_PREDEF true)
set(DOXYGEN_PREDEFINED
"__cplusplus"
"HAL_ENUM(name)=enum name : int32_t"
"DOXYGEN"
"WPI_NOEXCEPT:=noexcept"
"WPI_SFINAE(x):="
"WPI_REQUIRES(x):="
"WPI_REQUIRES_RET(...):="
"WPI_ENABLE_IF(...):="
"WPI_CONSTEXPR:=constexpr"
"WPI_CONSTEXPR_FNC:=constexpr"
"WPI_IMPL_DEFINED(...):=implementation_defined"
"WPI_EBO(...):="
)
execute_process(COMMAND git describe OUTPUT_VARIABLE version)
string(SUBSTRING ${version} 1 -1 version)
set(DOXYGEN_PROJECT_NUMBER ${version})
doxygen_add_docs(docs ${docs_dirs})
endmacro()

View File

@@ -55,9 +55,4 @@ macro(wpilib_target_warnings target)
)
target_compile_options(${target} PRIVATE -gz=zlib)
endif()
# Disable std::mutex constexpr constructor on MSVC
if(MSVC)
target_compile_options(${target} PRIVATE /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
endif()
endmacro()

View File

@@ -1,92 +0,0 @@
function(wpi_protobuf_generate)
set(_singleargs PROTOC_OUT_DIR PLUGIN DEPENDENCIES)
if(COMMAND target_sources)
list(APPEND _singleargs TARGET)
endif()
set(_multiargs PROTOS)
cmake_parse_arguments(
wpi_protobuf_generate
"${_options}"
"${_singleargs}"
"${_multiargs}"
"${ARGN}"
)
if(NOT wpi_protobuf_generate_PROTOS)
message(SEND_ERROR "Error: protobuf_generate called without any targets or source files")
return()
endif()
if(NOT wpi_protobuf_generate_TARGET)
message(SEND_ERROR "Error: wpi_protobuf_generate called without a target")
return()
endif()
if(NOT wpi_protobuf_generate_PROTOC_OUT_DIR)
message(SEND_ERROR "Error: protobuf_generate called without a protoc out directory")
return()
endif()
if(NOT wpi_protobuf_generate_PLUGIN)
message(SEND_ERROR "Error: wpi_protobuf_generate called without a plugin")
return()
endif()
set(_generate_extensions .pb.h .pb.cc)
# Create an include path for each file specified
foreach(_file ${wpi_protobuf_generate_PROTOS})
get_filename_component(_abs_file ${_file} ABSOLUTE)
get_filename_component(_abs_dir ${_abs_file} DIRECTORY)
list(FIND _protobuf_include_path ${_abs_dir} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${_abs_dir})
endif()
endforeach()
set(_generated_srcs_all)
foreach(_proto ${wpi_protobuf_generate_PROTOS})
get_filename_component(_abs_file ${_proto} ABSOLUTE)
get_filename_component(_abs_dir ${_abs_file} DIRECTORY)
get_filename_component(_basename ${_proto} NAME_WLE)
file(RELATIVE_PATH _rel_dir ${CMAKE_CURRENT_SOURCE_DIR} ${_abs_dir})
set(_possible_rel_dir)
set(_generated_srcs)
foreach(_ext ${_generate_extensions})
list(
APPEND
_generated_srcs
"${wpi_protobuf_generate_PROTOC_OUT_DIR}/${_possible_rel_dir}${_basename}${_ext}"
)
endforeach()
list(APPEND _generated_srcs_all ${_generated_srcs})
set(_comment "Running WPILib protocol buffer compiler on ${_proto}")
add_custom_command(
OUTPUT ${_generated_srcs}
COMMAND protobuf::protoc
ARGS
--cpp_out ${wpi_protobuf_generate_PROTOC_OUT_DIR} --wpilib_out
${wpi_protobuf_generate_PROTOC_OUT_DIR}
--plugin=protoc-gen-wpilib=${wpi_protobuf_generate_PLUGIN} ${_protobuf_include_path}
${_abs_file}
DEPENDS
${_abs_file}
protobuf::protoc
${wpi_protobuf_generate_DEPENDENCIES}
${wpi_protobuf_generate_PLUGIN}
COMMENT ${_comment}
VERBATIM
)
endforeach()
set_source_files_properties(${_generated_srcs_all} PROPERTIES GENERATED TRUE)
if(wpi_protobuf_generate_TARGET)
target_sources(${wpi_protobuf_generate_TARGET} PRIVATE ${_generated_srcs_all})
endif()
endfunction()

View File

@@ -1,4 +0,0 @@
set(GCC_COMPILER_VERSION "" CACHE STRING "GCC Compiler version")
set(GNU_MACHINE "arm-frc2022-linux-gnueabi" CACHE STRING "GNU compiler triple")
set(SOFTFP yes)
include("${CMAKE_CURRENT_LIST_DIR}/arm.toolchain.cmake")

View File

@@ -1,98 +0,0 @@
set(GCC_COMPILER_VERSION "" CACHE STRING "GCC Compiler version")
set(GNU_MACHINE "arm-raspbian10-linux-gnueabi" CACHE STRING "GNU compiler triple")
if(COMMAND toolchain_save_config)
return() # prevent recursive call
endif()
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
if(NOT DEFINED CMAKE_SYSTEM_PROCESSOR)
set(CMAKE_SYSTEM_PROCESSOR arm)
else()
#message("CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}")
endif()
include("${CMAKE_CURRENT_LIST_DIR}/opencv/platforms/linux/gnu.toolchain.cmake")
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm AND NOT ARM_IGNORE_FP)
set(FLOAT_ABI_SUFFIX "")
if(NOT SOFTFP)
set(FLOAT_ABI_SUFFIX "hf")
endif()
endif()
if(NOT "x${GCC_COMPILER_VERSION}" STREQUAL "x")
set(__GCC_VER_SUFFIX "-${GCC_COMPILER_VERSION}")
endif()
if(NOT DEFINED CMAKE_C_COMPILER)
find_program(CMAKE_C_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-gcc${__GCC_VER_SUFFIX})
else()
#message(WARNING "CMAKE_C_COMPILER=${CMAKE_C_COMPILER} is defined")
endif()
if(NOT DEFINED CMAKE_CXX_COMPILER)
find_program(CMAKE_CXX_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-g++${__GCC_VER_SUFFIX})
else()
#message(WARNING "CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} is defined")
endif()
if(NOT DEFINED CMAKE_LINKER)
find_program(CMAKE_LINKER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld)
else()
#message(WARNING "CMAKE_LINKER=${CMAKE_LINKER} is defined")
endif()
if(NOT DEFINED CMAKE_AR)
find_program(CMAKE_AR NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar)
else()
#message(WARNING "CMAKE_AR=${CMAKE_AR} is defined")
endif()
if(NOT DEFINED ARM_LINUX_SYSROOT AND DEFINED GNU_MACHINE)
set(ARM_LINUX_SYSROOT /usr/${GNU_MACHINE}${FLOAT_ABI_SUFFIX})
endif()
if(NOT DEFINED CMAKE_CXX_FLAGS)
set(CMAKE_CXX_FLAGS "" CACHE INTERNAL "")
set(CMAKE_C_FLAGS "" CACHE INTERNAL "")
set(CMAKE_SHARED_LINKER_FLAGS "" CACHE INTERNAL "")
set(CMAKE_MODULE_LINKER_FLAGS "" CACHE INTERNAL "")
set(CMAKE_EXE_LINKER_FLAGS "" CACHE INTERNAL "")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,nocopyreloc")
endif()
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
set(ARM_LINKER_FLAGS "-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
set(ARM_LINKER_FLAGS "-Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
endif()
set(CMAKE_SHARED_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}")
else()
#message(WARNING "CMAKE_CXX_FLAGS='${CMAKE_CXX_FLAGS}' is defined")
endif()
if(USE_NEON)
message(WARNING "You use obsolete variable USE_NEON to enable NEON instruction set. Use -DENABLE_NEON=ON instead." )
set(ENABLE_NEON TRUE)
elseif(USE_VFPV3)
message(WARNING "You use obsolete variable USE_VFPV3 to enable VFPV3 instruction set. Use -DENABLE_VFPV3=ON instead." )
set(ENABLE_VFPV3 TRUE)
endif()
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${ARM_LINUX_SYSROOT})
if(EXISTS ${CUDA_TOOLKIT_ROOT_DIR})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${CUDA_TOOLKIT_ROOT_DIR})
endif()
set(TOOLCHAIN_CONFIG_VARS ${TOOLCHAIN_CONFIG_VARS}
ARM_LINUX_SYSROOT
ENABLE_NEON
ENABLE_VFPV3
CUDA_TOOLKIT_ROOT_DIR
)
toolchain_save_config()

View File

@@ -1,97 +0,0 @@
if(COMMAND toolchain_save_config)
return() # prevent recursive call
endif()
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
if(NOT DEFINED CMAKE_SYSTEM_PROCESSOR)
set(CMAKE_SYSTEM_PROCESSOR arm)
else()
#message("CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}")
endif()
include("${CMAKE_CURRENT_LIST_DIR}/gnu.toolchain.cmake")
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm AND NOT ARM_IGNORE_FP)
set(FLOAT_ABI_SUFFIX "")
if(NOT SOFTFP)
set(FLOAT_ABI_SUFFIX "hf")
endif()
endif()
if(NOT "x${GCC_COMPILER_VERSION}" STREQUAL "x")
set(__GCC_VER_SUFFIX "-${GCC_COMPILER_VERSION}")
endif()
if(NOT DEFINED CMAKE_C_COMPILER)
find_program(CMAKE_C_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-gcc${__GCC_VER_SUFFIX})
else()
#message(WARNING "CMAKE_C_COMPILER=${CMAKE_C_COMPILER} is defined")
endif()
if(NOT DEFINED CMAKE_CXX_COMPILER)
find_program(CMAKE_CXX_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-g++${__GCC_VER_SUFFIX})
else()
#message(WARNING "CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} is defined")
endif()
if(NOT DEFINED CMAKE_LINKER)
find_program(CMAKE_LINKER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld)
else()
#message(WARNING "CMAKE_LINKER=${CMAKE_LINKER} is defined")
endif()
if(NOT DEFINED CMAKE_AR)
find_program(CMAKE_AR NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar)
else()
#message(WARNING "CMAKE_AR=${CMAKE_AR} is defined")
endif()
if(NOT DEFINED ARM_LINUX_SYSROOT AND DEFINED GNU_MACHINE)
set(ARM_LINUX_SYSROOT /usr/${GNU_MACHINE}${FLOAT_ABI_SUFFIX})
endif()
if(NOT DEFINED CMAKE_CXX_FLAGS)
set(CMAKE_CXX_FLAGS "" CACHE INTERNAL "")
set(CMAKE_C_FLAGS "" CACHE INTERNAL "")
set(CMAKE_SHARED_LINKER_FLAGS "" CACHE INTERNAL "")
set(CMAKE_MODULE_LINKER_FLAGS "" CACHE INTERNAL "")
set(CMAKE_EXE_LINKER_FLAGS "" CACHE INTERNAL "")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
set(CMAKE_CXX_FLAGS "-mthumb ${CMAKE_CXX_FLAGS}")
set(CMAKE_C_FLAGS "-mthumb ${CMAKE_C_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,nocopyreloc")
endif()
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
set(ARM_LINKER_FLAGS "-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
set(ARM_LINKER_FLAGS "-Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
endif()
set(CMAKE_SHARED_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}")
else()
#message(WARNING "CMAKE_CXX_FLAGS='${CMAKE_CXX_FLAGS}' is defined")
endif()
if(USE_NEON)
message(WARNING "You use obsolete variable USE_NEON to enable NEON instruction set. Use -DENABLE_NEON=ON instead." )
set(ENABLE_NEON TRUE)
elseif(USE_VFPV3)
message(WARNING "You use obsolete variable USE_VFPV3 to enable VFPV3 instruction set. Use -DENABLE_VFPV3=ON instead." )
set(ENABLE_VFPV3 TRUE)
endif()
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${ARM_LINUX_SYSROOT})
if(EXISTS ${CUDA_TOOLKIT_ROOT_DIR})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${CUDA_TOOLKIT_ROOT_DIR})
endif()
set(TOOLCHAIN_CONFIG_VARS ${TOOLCHAIN_CONFIG_VARS}
ARM_LINUX_SYSROOT
ENABLE_NEON
ENABLE_VFPV3
CUDA_TOOLKIT_ROOT_DIR
)
toolchain_save_config()

View File

@@ -1,134 +0,0 @@
cmake_minimum_required(VERSION 3.11)
# load settings in case of "try compile"
set(TOOLCHAIN_CONFIG_FILE "${WPILIB_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/toolchain.config.cmake")
get_property(__IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
if(__IN_TRY_COMPILE)
include("${CMAKE_CURRENT_SOURCE_DIR}/../toolchain.config.cmake" OPTIONAL) # WPILIB_BINARY_DIR is different
macro(toolchain_save_config)
# nothing
endmacro()
else()
macro(toolchain_save_config)
set(__config "#message(\"Load TOOLCHAIN config...\")\n")
get_cmake_property(__variableNames VARIABLES)
set(__vars_list ${ARGN})
list(APPEND __vars_list
${TOOLCHAIN_CONFIG_VARS}
CMAKE_SYSTEM_NAME
CMAKE_SYSTEM_VERSION
CMAKE_SYSTEM_PROCESSOR
CMAKE_C_COMPILER
CMAKE_CXX_COMPILER
CMAKE_C_FLAGS
CMAKE_CXX_FLAGS
CMAKE_SHARED_LINKER_FLAGS
CMAKE_MODULE_LINKER_FLAGS
CMAKE_EXE_LINKER_FLAGS
CMAKE_SKIP_RPATH
CMAKE_FIND_ROOT_PATH
GCC_COMPILER_VERSION
)
foreach(__var ${__variableNames})
foreach(_v ${__vars_list})
if("x${__var}" STREQUAL "x${_v}")
if(${__var} MATCHES " ")
set(__config "${__config}set(${__var} \"${${__var}}\")\n")
else()
set(__config "${__config}set(${__var} ${${__var}})\n")
endif()
endif()
endforeach()
endforeach()
if(EXISTS "${TOOLCHAIN_CONFIG_FILE}")
file(READ "${TOOLCHAIN_CONFIG_FILE}" __config_old)
endif()
if("${__config_old}" STREQUAL "${__config}")
# nothing
else()
#message("Update TOOLCHAIN config: ${__config}")
file(WRITE "${TOOLCHAIN_CONFIG_FILE}" "${__config}")
endif()
unset(__config)
unset(__config_old)
unset(__vars_list)
unset(__variableNames)
endmacro()
endif() # IN_TRY_COMPILE
if(NOT CMAKE_FIND_ROOT_PATH_MODE_LIBRARY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
endif()
if(NOT CMAKE_FIND_ROOT_PATH_MODE_INCLUDE)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()
if(NOT CMAKE_FIND_ROOT_PATH_MODE_PACKAGE)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
endif()
if(NOT CMAKE_FIND_ROOT_PATH_MODE_PROGRAM)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
endif()
macro(__cmake_find_root_save_and_reset)
foreach(v
CMAKE_FIND_ROOT_PATH_MODE_LIBRARY
CMAKE_FIND_ROOT_PATH_MODE_INCLUDE
CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
CMAKE_FIND_ROOT_PATH_MODE_PROGRAM
)
set(__save_${v} ${${v}})
set(${v} NEVER)
endforeach()
endmacro()
macro(__cmake_find_root_restore)
foreach(v
CMAKE_FIND_ROOT_PATH_MODE_LIBRARY
CMAKE_FIND_ROOT_PATH_MODE_INCLUDE
CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
CMAKE_FIND_ROOT_PATH_MODE_PROGRAM
)
set(${v} ${__save_${v}})
unset(__save_${v})
endforeach()
endmacro()
# macro to find programs on the host OS
macro(find_host_program)
__cmake_find_root_save_and_reset()
if(CMAKE_HOST_WIN32)
SET(WIN32 1)
SET(UNIX)
elseif(CMAKE_HOST_APPLE)
SET(APPLE 1)
SET(UNIX)
endif()
find_program(${ARGN})
SET(WIN32)
SET(APPLE)
SET(UNIX 1)
__cmake_find_root_restore()
endmacro()
# macro to find packages on the host OS
macro(find_host_package)
__cmake_find_root_save_and_reset()
if(CMAKE_HOST_WIN32)
SET(WIN32 1)
SET(UNIX)
elseif(CMAKE_HOST_APPLE)
SET(APPLE 1)
SET(UNIX)
endif()
find_package(${ARGN})
SET(WIN32)
SET(APPLE)
SET(UNIX 1)
__cmake_find_root_restore()
endmacro()
set(CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries.")

21
cscore/BUILD.bazel Normal file
View File

@@ -0,0 +1,21 @@
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
java_library(
name = "cscore-java",
srcs = glob(["src/main/java/**/*.java"]),
visibility = ["//visibility:public"],
deps = [
"//wpiutil:wpiutil-java",
"@bzlmodrio-opencv//libraries/java/opencv",
],
)
java_binary(
name = "DevMain-Java",
srcs = ["src/dev/java/edu/wpi/first/cscore/DevMain.java"],
main_class = "edu.wpi.first.cscore.DevMain",
deps = [
":cscore-java",
"//wpiutil:wpiutil-java",
],
)

View File

@@ -164,19 +164,6 @@ run {
nativeUtils.exportsConfigs {
cscore {
x64ExcludeSymbols = [
'_CT??_R0?AV_System_error',
'_CT??_R0?AVexception',
'_CT??_R0?AVfailure',
'_CT??_R0?AVruntime_error',
'_CT??_R0?AVsystem_error',
'_CTA5?AVfailure',
'_TI5?AVfailure',
'_CT??_R0?AVout_of_range',
'_CTA3?AVout_of_range',
'_TI3?AVout_of_range',
'_CT??_R0?AVbad_cast'
]
}
cscoreJNI {
x64SymbolFilter = symbolFilter

View File

@@ -9,6 +9,7 @@
#include <utility>
#include <vector>
#include <fmt/format.h>
#include <wpi/MemAlloc.h>
#include <wpi/StringExtras.h>
#include <wpi/timestamp.h>
@@ -294,12 +295,27 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::raw_istream& is,
return false;
}
unsigned int contentLength = 0;
int width, height;
if (auto v = wpi::parse_integer<unsigned int>(contentLengthBuf, 10)) {
contentLength = v.value();
// We know how big it is! Just get a frame of the right size and read
// the data directly into it.
unsigned int contentLength = v.value();
auto image =
AllocImage(VideoMode::PixelFormat::kMJPEG, 0, 0, contentLength);
is.read(image->data(), contentLength);
if (!m_active || is.has_error()) {
return false;
}
if (!GetJpegSize(image->str(), &width, &height)) {
SWARNING("did not receive a JPEG image");
PutError("did not receive a JPEG image", wpi::Now());
return false;
}
image->width = width;
image->height = height;
PutFrame(std::move(image), wpi::Now());
} else {
// Ugh, no Content-Length? Read the blocks of the JPEG file.
int width, height;
if (!ReadJpeg(is, imageBuf, &width, &height)) {
SWARNING("did not receive a JPEG image");
PutError("did not receive a JPEG image", wpi::Now());
@@ -307,27 +323,18 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::raw_istream& is,
}
PutFrame(VideoMode::PixelFormat::kMJPEG, width, height, imageBuf,
wpi::Now());
++m_frameCount;
return true;
}
// We know how big it is! Just get a frame of the right size and read
// the data directly into it.
auto image = AllocImage(VideoMode::PixelFormat::kMJPEG, 0, 0, contentLength);
is.read(image->data(), contentLength);
if (!m_active || is.has_error()) {
return false;
}
int width, height;
if (!GetJpegSize(image->str(), &width, &height)) {
SWARNING("did not receive a JPEG image");
PutError("did not receive a JPEG image", wpi::Now());
return false;
}
image->width = width;
image->height = height;
PutFrame(std::move(image), wpi::Now());
++m_frameCount;
// update video mode if not set
std::scoped_lock lock(m_mutex);
if (m_mode.pixelFormat != VideoMode::PixelFormat::kMJPEG ||
m_mode.width == 0 || m_mode.height == 0) {
m_mode.pixelFormat = VideoMode::PixelFormat::kMJPEG;
m_mode.width = width;
m_mode.height = height;
}
return true;
}
@@ -518,6 +525,14 @@ bool HttpCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) {
}
std::scoped_lock lock(m_mutex);
m_mode = mode;
m_streamSettings.clear();
if (mode.width != 0 && mode.height != 0) {
m_streamSettings["resolution"] =
fmt::format("{}x{}", mode.width, mode.height);
}
if (mode.fps != 0) {
m_streamSettings["fps"] = fmt::format("{}", mode.fps);
}
m_streamSettingsUpdated = true;
return true;
}

View File

@@ -15,7 +15,6 @@
#include <thread>
#include <vector>
#include <wpi/SmallString.h>
#include <wpi/StringMap.h>
#include <wpi/condition_variable.h>
#include <wpi/raw_istream.h>
@@ -136,10 +135,10 @@ class HttpCameraImpl : public SourceImpl {
wpi::condition_variable m_sinkEnabledCond;
wpi::StringMap<wpi::SmallString<16>> m_settings;
wpi::StringMap<std::string> m_settings;
wpi::condition_variable m_settingsCond;
wpi::StringMap<wpi::SmallString<16>> m_streamSettings;
wpi::StringMap<std::string> m_streamSettings;
std::atomic_bool m_streamSettingsUpdated{false};
wpi::condition_variable m_monitorCond;

View File

@@ -98,6 +98,9 @@ void Instance::DestroySource(CS_Source handle) {
void Instance::DestroySink(CS_Sink handle) {
if (auto data = m_sinks.Free(handle)) {
if (auto source = data->sink->GetSource()) {
source->Wakeup();
}
notifier.NotifySink(data->sink->GetName(), handle, CS_SINK_DESTROYED);
}
}

View File

@@ -79,7 +79,8 @@ Frame SourceImpl::GetCurFrame() {
Frame SourceImpl::GetNextFrame() {
std::unique_lock lock{m_frameMutex};
auto oldTime = m_frame.GetTime();
m_frameCv.wait(lock, [=, this] { return m_frame.GetTime() != oldTime; });
m_frameCv.wait(
lock, [=, this] { return oldTime == 0 || m_frame.GetTime() != oldTime; });
return m_frame;
}

View File

@@ -34,7 +34,7 @@ class Telemetry::Thread : public wpi::SafeThread {
int64_t Telemetry::Thread::GetValue(CS_Handle handle, CS_TelemetryKind kind,
CS_Status* status) {
auto it = m_user.find(std::make_pair(handle, static_cast<int>(kind)));
auto it = m_user.find(std::pair{handle, static_cast<int>(kind)});
if (it == m_user.end()) {
*status = CS_EMPTY_VALUE;
return 0;
@@ -136,8 +136,8 @@ void Telemetry::RecordSourceBytes(const SourceImpl& source, int quantity) {
return;
}
auto handleData = Instance::GetInstance().FindSource(source);
thr->m_current[std::make_pair(Handle{handleData.first, Handle::kSource},
static_cast<int>(CS_SOURCE_BYTES_RECEIVED))] +=
thr->m_current[std::pair{Handle{handleData.first, Handle::kSource},
static_cast<int>(CS_SOURCE_BYTES_RECEIVED)}] +=
quantity;
}
@@ -147,7 +147,7 @@ void Telemetry::RecordSourceFrames(const SourceImpl& source, int quantity) {
return;
}
auto handleData = Instance::GetInstance().FindSource(source);
thr->m_current[std::make_pair(Handle{handleData.first, Handle::kSource},
static_cast<int>(CS_SOURCE_FRAMES_RECEIVED))] +=
thr->m_current[std::pair{Handle{handleData.first, Handle::kSource},
static_cast<int>(CS_SOURCE_FRAMES_RECEIVED)}] +=
quantity;
}

View File

@@ -183,10 +183,10 @@ UnlimitedHandleResource<THandle, TStruct, typeValue, TMutex>::FindIf(F func) {
for (size_t i = 0; i < m_structures.size(); i++) {
auto& structure = m_structures[i];
if (structure != nullptr && func(*structure)) {
return std::make_pair(MakeHandle(i), structure);
return std::pair{MakeHandle(i), structure};
}
}
return std::make_pair(0, nullptr);
return std::pair{0, nullptr};
}
} // namespace cs

File diff suppressed because it is too large Load Diff

View File

@@ -1,649 +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.
#ifndef CSCORE_CSCORE_OO_INC_
#define CSCORE_CSCORE_OO_INC_
#include <functional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <wpi/deprecated.h>
#include "cscore_oo.h"
namespace cs {
inline std::string VideoProperty::GetName() const {
m_status = 0;
return GetPropertyName(m_handle, &m_status);
}
inline int VideoProperty::Get() const {
m_status = 0;
return GetProperty(m_handle, &m_status);
}
inline void VideoProperty::Set(int value) {
m_status = 0;
SetProperty(m_handle, value, &m_status);
}
inline int VideoProperty::GetMin() const {
m_status = 0;
return GetPropertyMin(m_handle, &m_status);
}
inline int VideoProperty::GetMax() const {
m_status = 0;
return GetPropertyMax(m_handle, &m_status);
}
inline int VideoProperty::GetStep() const {
m_status = 0;
return GetPropertyStep(m_handle, &m_status);
}
inline int VideoProperty::GetDefault() const {
m_status = 0;
return GetPropertyDefault(m_handle, &m_status);
}
inline std::string VideoProperty::GetString() const {
m_status = 0;
return GetStringProperty(m_handle, &m_status);
}
inline std::string_view VideoProperty::GetString(
wpi::SmallVectorImpl<char>& buf) const {
m_status = 0;
return GetStringProperty(m_handle, buf, &m_status);
}
inline void VideoProperty::SetString(std::string_view value) {
m_status = 0;
SetStringProperty(m_handle, value, &m_status);
}
inline std::vector<std::string> VideoProperty::GetChoices() const {
m_status = 0;
return GetEnumPropertyChoices(m_handle, &m_status);
}
inline VideoProperty::VideoProperty(CS_Property handle) : m_handle(handle) {
m_status = 0;
if (handle == 0) {
m_kind = kNone;
} else {
m_kind =
static_cast<Kind>(static_cast<int>(GetPropertyKind(handle, &m_status)));
}
}
inline VideoProperty::VideoProperty(CS_Property handle, Kind kind)
: m_handle(handle), m_kind(kind) {}
inline VideoSource::VideoSource(const VideoSource& source)
: m_handle(source.m_handle == 0 ? 0
: CopySource(source.m_handle, &m_status)) {}
inline VideoSource::VideoSource(VideoSource&& other) noexcept : VideoSource() {
swap(*this, other);
}
inline VideoSource& VideoSource::operator=(VideoSource other) noexcept {
swap(*this, other);
return *this;
}
inline VideoSource::~VideoSource() {
m_status = 0;
if (m_handle != 0) {
ReleaseSource(m_handle, &m_status);
}
}
inline VideoSource::Kind VideoSource::GetKind() const {
m_status = 0;
return static_cast<VideoSource::Kind>(GetSourceKind(m_handle, &m_status));
}
inline std::string VideoSource::GetName() const {
m_status = 0;
return GetSourceName(m_handle, &m_status);
}
inline std::string VideoSource::GetDescription() const {
m_status = 0;
return GetSourceDescription(m_handle, &m_status);
}
inline uint64_t VideoSource::GetLastFrameTime() const {
m_status = 0;
return GetSourceLastFrameTime(m_handle, &m_status);
}
inline void VideoSource::SetConnectionStrategy(ConnectionStrategy strategy) {
m_status = 0;
SetSourceConnectionStrategy(
m_handle, static_cast<CS_ConnectionStrategy>(static_cast<int>(strategy)),
&m_status);
}
inline bool VideoSource::IsConnected() const {
m_status = 0;
return IsSourceConnected(m_handle, &m_status);
}
inline bool VideoSource::IsEnabled() const {
m_status = 0;
return IsSourceEnabled(m_handle, &m_status);
}
inline VideoProperty VideoSource::GetProperty(std::string_view name) {
m_status = 0;
return VideoProperty{GetSourceProperty(m_handle, name, &m_status)};
}
inline VideoMode VideoSource::GetVideoMode() const {
m_status = 0;
return GetSourceVideoMode(m_handle, &m_status);
}
inline bool VideoSource::SetVideoMode(const VideoMode& mode) {
m_status = 0;
return SetSourceVideoMode(m_handle, mode, &m_status);
}
inline bool VideoSource::SetVideoMode(VideoMode::PixelFormat pixelFormat,
int width, int height, int fps) {
m_status = 0;
return SetSourceVideoMode(
m_handle, VideoMode{pixelFormat, width, height, fps}, &m_status);
}
inline bool VideoSource::SetPixelFormat(VideoMode::PixelFormat pixelFormat) {
m_status = 0;
return SetSourcePixelFormat(m_handle, pixelFormat, &m_status);
}
inline bool VideoSource::SetResolution(int width, int height) {
m_status = 0;
return SetSourceResolution(m_handle, width, height, &m_status);
}
inline bool VideoSource::SetFPS(int fps) {
m_status = 0;
return SetSourceFPS(m_handle, fps, &m_status);
}
inline bool VideoSource::SetConfigJson(std::string_view config) {
m_status = 0;
return SetSourceConfigJson(m_handle, config, &m_status);
}
inline bool VideoSource::SetConfigJson(const wpi::json& config) {
m_status = 0;
return SetSourceConfigJson(m_handle, config, &m_status);
}
inline std::string VideoSource::GetConfigJson() const {
m_status = 0;
return GetSourceConfigJson(m_handle, &m_status);
}
inline double VideoSource::GetActualFPS() const {
m_status = 0;
return cs::GetTelemetryAverageValue(m_handle, CS_SOURCE_FRAMES_RECEIVED,
&m_status);
}
inline double VideoSource::GetActualDataRate() const {
m_status = 0;
return cs::GetTelemetryAverageValue(m_handle, CS_SOURCE_BYTES_RECEIVED,
&m_status);
}
inline std::vector<VideoMode> VideoSource::EnumerateVideoModes() const {
CS_Status status = 0;
return EnumerateSourceVideoModes(m_handle, &status);
}
inline void VideoCamera::SetBrightness(int brightness) {
m_status = 0;
SetCameraBrightness(m_handle, brightness, &m_status);
}
inline int VideoCamera::GetBrightness() {
m_status = 0;
return GetCameraBrightness(m_handle, &m_status);
}
inline void VideoCamera::SetWhiteBalanceAuto() {
m_status = 0;
SetCameraWhiteBalanceAuto(m_handle, &m_status);
}
inline void VideoCamera::SetWhiteBalanceHoldCurrent() {
m_status = 0;
SetCameraWhiteBalanceHoldCurrent(m_handle, &m_status);
}
inline void VideoCamera::SetWhiteBalanceManual(int value) {
m_status = 0;
SetCameraWhiteBalanceManual(m_handle, value, &m_status);
}
inline void VideoCamera::SetExposureAuto() {
m_status = 0;
SetCameraExposureAuto(m_handle, &m_status);
}
inline void VideoCamera::SetExposureHoldCurrent() {
m_status = 0;
SetCameraExposureHoldCurrent(m_handle, &m_status);
}
inline void VideoCamera::SetExposureManual(int value) {
m_status = 0;
SetCameraExposureManual(m_handle, value, &m_status);
}
inline UsbCamera::UsbCamera(std::string_view name, int dev) {
m_handle = CreateUsbCameraDev(name, dev, &m_status);
}
inline UsbCamera::UsbCamera(std::string_view name, std::string_view path) {
m_handle = CreateUsbCameraPath(name, path, &m_status);
}
inline std::vector<UsbCameraInfo> UsbCamera::EnumerateUsbCameras() {
CS_Status status = 0;
return ::cs::EnumerateUsbCameras(&status);
}
inline void UsbCamera::SetPath(std::string_view path) {
m_status = 0;
return ::cs::SetUsbCameraPath(m_handle, path, &m_status);
}
inline std::string UsbCamera::GetPath() const {
m_status = 0;
return ::cs::GetUsbCameraPath(m_handle, &m_status);
}
inline UsbCameraInfo UsbCamera::GetInfo() const {
m_status = 0;
return ::cs::GetUsbCameraInfo(m_handle, &m_status);
}
inline void UsbCamera::SetConnectVerbose(int level) {
m_status = 0;
SetProperty(GetSourceProperty(m_handle, "connect_verbose", &m_status), level,
&m_status);
}
inline HttpCamera::HttpCamera(std::string_view name, std::string_view url,
HttpCameraKind kind) {
m_handle = CreateHttpCamera(
name, url, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
&m_status);
}
inline HttpCamera::HttpCamera(std::string_view name, const char* url,
HttpCameraKind kind) {
m_handle = CreateHttpCamera(
name, url, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
&m_status);
}
inline HttpCamera::HttpCamera(std::string_view name, const std::string& url,
HttpCameraKind kind)
: HttpCamera(name, std::string_view{url}, kind) {}
inline HttpCamera::HttpCamera(std::string_view name,
std::span<const std::string> urls,
HttpCameraKind kind) {
m_handle = CreateHttpCamera(
name, urls, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
&m_status);
}
template <typename T>
inline HttpCamera::HttpCamera(std::string_view name,
std::initializer_list<T> urls,
HttpCameraKind kind) {
std::vector<std::string> vec;
vec.reserve(urls.size());
for (const auto& url : urls) {
vec.emplace_back(url);
}
m_handle = CreateHttpCamera(
name, vec, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
&m_status);
}
inline HttpCamera::HttpCameraKind HttpCamera::GetHttpCameraKind() const {
m_status = 0;
return static_cast<HttpCameraKind>(
static_cast<int>(::cs::GetHttpCameraKind(m_handle, &m_status)));
}
inline void HttpCamera::SetUrls(std::span<const std::string> urls) {
m_status = 0;
::cs::SetHttpCameraUrls(m_handle, urls, &m_status);
}
template <typename T>
inline void HttpCamera::SetUrls(std::initializer_list<T> urls) {
std::vector<std::string> vec;
vec.reserve(urls.size());
for (const auto& url : urls) {
vec.emplace_back(url);
}
m_status = 0;
::cs::SetHttpCameraUrls(m_handle, vec, &m_status);
}
inline std::vector<std::string> HttpCamera::GetUrls() const {
m_status = 0;
return ::cs::GetHttpCameraUrls(m_handle, &m_status);
}
WPI_IGNORE_DEPRECATED
inline std::vector<std::string> AxisCamera::HostToUrl(
std::span<const std::string> hosts) {
std::vector<std::string> rv;
rv.reserve(hosts.size());
for (const auto& host : hosts) {
rv.emplace_back(HostToUrl(std::string_view{host}));
}
return rv;
}
template <typename T>
inline std::vector<std::string> AxisCamera::HostToUrl(
std::initializer_list<T> hosts) {
std::vector<std::string> rv;
rv.reserve(hosts.size());
for (const auto& host : hosts) {
rv.emplace_back(HostToUrl(std::string_view{host}));
}
return rv;
}
inline AxisCamera::AxisCamera(std::string_view name, std::string_view host)
: HttpCamera(name, HostToUrl(host), kAxis) {}
inline AxisCamera::AxisCamera(std::string_view name, const char* host)
: HttpCamera(name, HostToUrl(host), kAxis) {}
inline AxisCamera::AxisCamera(std::string_view name, const std::string& host)
: HttpCamera(name, HostToUrl(std::string_view{host}), kAxis) {}
inline AxisCamera::AxisCamera(std::string_view name,
std::span<const std::string> hosts)
: HttpCamera(name, HostToUrl(hosts), kAxis) {}
template <typename T>
inline AxisCamera::AxisCamera(std::string_view name,
std::initializer_list<T> hosts)
: HttpCamera(name, HostToUrl(hosts), kAxis) {}
WPI_UNIGNORE_DEPRECATED
inline void ImageSource::NotifyError(std::string_view msg) {
m_status = 0;
NotifySourceError(m_handle, msg, &m_status);
}
inline void ImageSource::SetConnected(bool connected) {
m_status = 0;
SetSourceConnected(m_handle, connected, &m_status);
}
inline void ImageSource::SetDescription(std::string_view description) {
m_status = 0;
SetSourceDescription(m_handle, description, &m_status);
}
inline VideoProperty ImageSource::CreateProperty(std::string_view name,
VideoProperty::Kind kind,
int minimum, int maximum,
int step, int defaultValue,
int value) {
m_status = 0;
return VideoProperty{CreateSourceProperty(
m_handle, name, static_cast<CS_PropertyKind>(static_cast<int>(kind)),
minimum, maximum, step, defaultValue, value, &m_status)};
}
inline VideoProperty ImageSource::CreateIntegerProperty(std::string_view name,
int minimum,
int maximum, int step,
int defaultValue,
int value) {
m_status = 0;
return VideoProperty{CreateSourceProperty(
m_handle, name,
static_cast<CS_PropertyKind>(
static_cast<int>(VideoProperty::Kind::kInteger)),
minimum, maximum, step, defaultValue, value, &m_status)};
}
inline VideoProperty ImageSource::CreateBooleanProperty(std::string_view name,
bool defaultValue,
bool value) {
m_status = 0;
return VideoProperty{CreateSourceProperty(
m_handle, name,
static_cast<CS_PropertyKind>(
static_cast<int>(VideoProperty::Kind::kBoolean)),
0, 1, 1, defaultValue ? 1 : 0, value ? 1 : 0, &m_status)};
}
inline VideoProperty ImageSource::CreateStringProperty(std::string_view name,
std::string_view value) {
m_status = 0;
auto prop = VideoProperty{
CreateSourceProperty(m_handle, name,
static_cast<CS_PropertyKind>(
static_cast<int>(VideoProperty::Kind::kString)),
0, 0, 0, 0, 0, &m_status)};
prop.SetString(value);
return prop;
}
inline void ImageSource::SetEnumPropertyChoices(
const VideoProperty& property, std::span<const std::string> choices) {
m_status = 0;
SetSourceEnumPropertyChoices(m_handle, property.m_handle, choices, &m_status);
}
template <typename T>
inline void ImageSource::SetEnumPropertyChoices(
const VideoProperty& property, std::initializer_list<T> choices) {
std::vector<std::string> vec;
vec.reserve(choices.size());
for (const auto& choice : choices) {
vec.emplace_back(choice);
}
m_status = 0;
SetSourceEnumPropertyChoices(m_handle, property.m_handle, vec, &m_status);
}
inline VideoSink::VideoSink(const VideoSink& sink)
: m_handle(sink.m_handle == 0 ? 0 : CopySink(sink.m_handle, &m_status)) {}
inline VideoSink::VideoSink(VideoSink&& other) noexcept : VideoSink() {
swap(*this, other);
}
inline VideoSink& VideoSink::operator=(VideoSink other) noexcept {
swap(*this, other);
return *this;
}
inline VideoSink::~VideoSink() {
m_status = 0;
if (m_handle != 0) {
ReleaseSink(m_handle, &m_status);
}
}
inline VideoSink::Kind VideoSink::GetKind() const {
m_status = 0;
return static_cast<VideoSink::Kind>(GetSinkKind(m_handle, &m_status));
}
inline std::string VideoSink::GetName() const {
m_status = 0;
return GetSinkName(m_handle, &m_status);
}
inline std::string VideoSink::GetDescription() const {
m_status = 0;
return GetSinkDescription(m_handle, &m_status);
}
inline VideoProperty VideoSink::GetProperty(std::string_view name) {
m_status = 0;
return VideoProperty{GetSinkProperty(m_handle, name, &m_status)};
}
inline void VideoSink::SetSource(VideoSource source) {
m_status = 0;
if (!source) {
SetSinkSource(m_handle, 0, &m_status);
} else {
SetSinkSource(m_handle, source.m_handle, &m_status);
}
}
inline VideoSource VideoSink::GetSource() const {
m_status = 0;
auto handle = GetSinkSource(m_handle, &m_status);
return VideoSource{handle == 0 ? 0 : CopySource(handle, &m_status)};
}
inline VideoProperty VideoSink::GetSourceProperty(std::string_view name) {
m_status = 0;
return VideoProperty{GetSinkSourceProperty(m_handle, name, &m_status)};
}
inline bool VideoSink::SetConfigJson(std::string_view config) {
m_status = 0;
return SetSinkConfigJson(m_handle, config, &m_status);
}
inline bool VideoSink::SetConfigJson(const wpi::json& config) {
m_status = 0;
return SetSinkConfigJson(m_handle, config, &m_status);
}
inline std::string VideoSink::GetConfigJson() const {
m_status = 0;
return GetSinkConfigJson(m_handle, &m_status);
}
inline MjpegServer::MjpegServer(std::string_view name,
std::string_view listenAddress, int port) {
m_handle = CreateMjpegServer(name, listenAddress, port, &m_status);
}
inline std::string MjpegServer::GetListenAddress() const {
m_status = 0;
return cs::GetMjpegServerListenAddress(m_handle, &m_status);
}
inline int MjpegServer::GetPort() const {
m_status = 0;
return cs::GetMjpegServerPort(m_handle, &m_status);
}
inline void MjpegServer::SetResolution(int width, int height) {
m_status = 0;
SetProperty(GetSinkProperty(m_handle, "width", &m_status), width, &m_status);
SetProperty(GetSinkProperty(m_handle, "height", &m_status), height,
&m_status);
}
inline void MjpegServer::SetFPS(int fps) {
m_status = 0;
SetProperty(GetSinkProperty(m_handle, "fps", &m_status), fps, &m_status);
}
inline void MjpegServer::SetCompression(int quality) {
m_status = 0;
SetProperty(GetSinkProperty(m_handle, "compression", &m_status), quality,
&m_status);
}
inline void MjpegServer::SetDefaultCompression(int quality) {
m_status = 0;
SetProperty(GetSinkProperty(m_handle, "default_compression", &m_status),
quality, &m_status);
}
inline void ImageSink::SetDescription(std::string_view description) {
m_status = 0;
SetSinkDescription(m_handle, description, &m_status);
}
inline std::string ImageSink::GetError() const {
m_status = 0;
return GetSinkError(m_handle, &m_status);
}
inline void ImageSink::SetEnabled(bool enabled) {
m_status = 0;
SetSinkEnabled(m_handle, enabled, &m_status);
}
inline VideoSource VideoEvent::GetSource() const {
CS_Status status = 0;
return VideoSource{sourceHandle == 0 ? 0 : CopySource(sourceHandle, &status)};
}
inline VideoSink VideoEvent::GetSink() const {
CS_Status status = 0;
return VideoSink{sinkHandle == 0 ? 0 : CopySink(sinkHandle, &status)};
}
inline VideoProperty VideoEvent::GetProperty() const {
return VideoProperty{propertyHandle,
static_cast<VideoProperty::Kind>(propertyKind)};
}
inline VideoListener::VideoListener(
std::function<void(const VideoEvent& event)> callback, int eventMask,
bool immediateNotify) {
CS_Status status = 0;
m_handle = AddListener(
[=](const RawEvent& event) {
callback(static_cast<const VideoEvent&>(event));
},
eventMask, immediateNotify, &status);
}
inline VideoListener::VideoListener(VideoListener&& other) noexcept
: VideoListener() {
swap(*this, other);
}
inline VideoListener& VideoListener::operator=(VideoListener&& other) noexcept {
swap(*this, other);
return *this;
}
inline VideoListener::~VideoListener() {
CS_Status status = 0;
if (m_handle != 0) {
RemoveListener(m_handle, &status);
}
}
} // namespace cs
#endif // CSCORE_CSCORE_OO_INC_

View File

@@ -75,25 +75,6 @@ size_t File::Read(void* buf, uint32_t count) {
return rv;
}
File::AsyncId File::AsyncReadBegin(uint32_t len) const {
int rv = sftp_async_read_begin(m_handle, len);
if (rv < 0) {
throw Exception{m_handle->sftp};
}
return rv;
}
size_t File::AsyncRead(void* data, uint32_t len, AsyncId id) {
auto rv = sftp_async_read(m_handle, data, len, id);
if (rv == SSH_ERROR) {
throw Exception{ssh_get_error(m_handle->sftp->session)};
}
if (rv == SSH_AGAIN) {
return 0;
}
return rv;
}
size_t File::Write(std::span<const uint8_t> data) {
auto rv = sftp_write(m_handle, data.data(), data.size());
if (rv < 0) {

View File

@@ -47,11 +47,7 @@ class File {
void SetNonblocking() { sftp_file_set_nonblocking(m_handle); }
void SetBlocking() { sftp_file_set_blocking(m_handle); }
using AsyncId = uint32_t;
size_t Read(void* buf, uint32_t count);
AsyncId AsyncReadBegin(uint32_t len) const;
size_t AsyncRead(void* data, uint32_t len, AsyncId id);
size_t Write(std::span<const uint8_t> data);
void Seek(uint64_t offset);

View File

@@ -1,6 +1,6 @@
plugins {
id 'java'
id "org.ysb33r.doxygen" version "1.0.3"
id "org.ysb33r.doxygen" version "1.0.4"
}
evaluationDependsOn(':apriltag')

View File

@@ -95,7 +95,10 @@ public class AnnotationProcessor extends AbstractProcessor {
// in this list will determine how it gets logged.
m_handlers =
List.of(
new LoggableHandler(processingEnv), // prioritize epilogue logging over Sendable
new LoggableHandler(
processingEnv,
roundEnv.getElementsAnnotatedWith(
Logged.class)), // prioritize epilogue logging over Sendable
new ConfiguredLoggerHandler(
processingEnv, customLoggers), // then customized logging configs
new ArrayHandler(processingEnv),

View File

@@ -61,15 +61,46 @@ public abstract class ElementHandler {
* @return the name specified in the {@link Logged @Logged} annotation on the element, if present;
* otherwise, the field or method's name with no modifications
*/
public String loggedName(Element element) {
var elementName = element.getSimpleName().toString();
var config = element.getAnnotation(Logged.class);
public static String loggedName(Element element) {
var elementConfig = element.getAnnotation(Logged.class);
if (config != null && !config.name().isBlank()) {
return config.name();
} else {
return elementName;
// Use the name provided on the logged element, if one is present
if (elementConfig != null && !elementConfig.name().isBlank()) {
return elementConfig.name();
}
var config = elementConfig;
if (config == null) {
// Look up the parent class configuration
// We assume one is present, since logged elements should only be found if the enclosing class
// is @Logged itself
Logged parentConfig = null;
for (var parent = element.getEnclosingElement();
parent != null;
parent = parent.getEnclosingElement()) {
parentConfig = parent.getAnnotation(Logged.class);
if (parentConfig != null) {
break;
}
}
config = parentConfig;
}
if (config == null) {
// Uh oh
throw new IllegalStateException(
"Could not generate a name for element "
+ element
+ " without a @Logged annotation AND without being contained within a class with a @Logged annotation!\n\nOpen an issue at https://github.com/wpilibsuite/allwpilib/issues and include a copy of the file that caused this error.");
}
var elementName = element.getSimpleName().toString();
return switch (config.defaultNaming()) {
case USE_CODE_NAME -> elementName;
case USE_HUMAN_NAME -> StringUtils.toHumanName(elementName);
};
}
/**

View File

@@ -176,7 +176,7 @@ public class EpilogueGenerator {
out.println(" config.loggingPeriod = Seconds.of(robot.getPeriod());");
out.println(" }");
out.println(" if (config.loggingPeriodOffset == null) {");
out.println(" config.loggingPeriodOffset = config.loggingPeriod.divide(2);");
out.println(" config.loggingPeriodOffset = config.loggingPeriod.div(2);");
out.println(" }");
out.println();
out.println(" robot.addPeriodic(() -> {");

View File

@@ -5,15 +5,32 @@
package edu.wpi.first.epilogue.processor;
import edu.wpi.first.epilogue.Logged;
import java.util.Collection;
import java.util.Comparator;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
/** Handles logging for types annotated with the {@link Logged @Logged} annotation. */
public class LoggableHandler extends ElementHandler {
protected LoggableHandler(ProcessingEnvironment processingEnv) {
private final Set<TypeElement> m_loggedTypes;
protected LoggableHandler(
ProcessingEnvironment processingEnv, Collection<? extends Element> loggedTypes) {
super(processingEnv);
m_loggedTypes =
loggedTypes.stream()
.filter(e -> e instanceof TypeElement)
.map(e -> (TypeElement) e)
.collect(Collectors.toSet());
}
@Override
@@ -27,17 +44,165 @@ public class LoggableHandler extends ElementHandler {
@Override
public String logInvocation(Element element) {
TypeMirror dataType = dataType(element);
var reflectedType =
var declaredType =
m_processingEnv
.getElementUtils()
.getTypeElement(m_processingEnv.getTypeUtils().erasure(dataType).toString());
return "Epilogue."
+ StringUtils.loggerFieldName(reflectedType)
+ ".tryUpdate(dataLogger.getSubLogger(\""
+ loggedName(element)
+ "\"), "
+ elementAccess(element)
+ ", Epilogue.getConfig().errorHandler)";
// Get the list of known loggable subtypes of the input type. This will include the input type.
// These are sorted by their distance from the declared type such that "more concrete" types are
// checked first so the instanceof chain doesn't check a really generic type first, even if a
// more specific loggable type could be used instead.
var loggableSubtypes =
m_loggedTypes.stream()
.filter(
l -> m_processingEnv.getTypeUtils().isAssignable(l.asType(), declaredType.asType()))
.sorted(inheritanceComparatorFor(declaredType))
.toList();
int size = loggableSubtypes.size();
// If there are no known loggable subtypes, return just the single logger call
if (size == 1) {
return generateLoggerCall(element, declaredType, elementAccess(element));
}
// Otherwise, generate an if-else chain to compare the element with its known loggable subtypes
// and implementations. A subclass without a @Logged annotation will be logged at runtime using
// the generic logger for whatever the field or method's declared type is.
String varName = cacheVariableName(element);
StringBuilder builder = new StringBuilder();
// Cache the value in a variable so it's only read once
builder.append("var %s = %s;\n".formatted(varName, elementAccess(element)));
for (int i = 0; i < size; i++) {
TypeElement type = loggableSubtypes.get(i);
String part;
if (i == 0) {
// First invocation, generate an "if" statement
part = generateIf(type, element, "if", varName);
} else if (i == size - 1) {
// Final invocation, generate an "else" statement
String loggerCall = generateLoggerCall(element, type, varName);
part =
" else {\n // Base type %s\n %s;\n}"
.formatted(declaredType.getQualifiedName(), loggerCall);
} else {
// Somewhere in the middle, generate an "else if" statement
part = generateIf(type, element, " else if", varName);
}
builder.append(part);
}
return builder.toString();
}
/**
* Generates the name of the cache variable to use for a logged element.
*
* @param element the logged element
* @return the cache variable name
*/
private static String cacheVariableName(Element element) {
// Generate unique names in case a field and a method share the same name
if (element instanceof VariableElement) {
return "$$%s".formatted(element.getSimpleName().toString());
} else if (element instanceof ExecutableElement) {
return "__%s".formatted(element.getSimpleName().toString());
} else {
// Generic fallback (shouldn't get here, since only fields and methods are logged)
return element.getSimpleName().toString();
}
}
/**
* Creates a comparator for sorting inheritors of a given type by their distance (farthest first)
* for use in generating if-else instanceof chains. Inheritors at the same distance from the base
* type will be further compared so classes come before interfaces, any any further ties are
* broken alphabetically by fully-qualified type names.
*
* @param declaredType the declared type
* @return the comparator
*/
private Comparator<TypeElement> inheritanceComparatorFor(TypeElement declaredType) {
Comparator<TypeElement> byDistance =
Comparator.comparingInt(
inheritor -> {
return inheritanceDistance(inheritor.asType(), declaredType.asType());
});
return byDistance
.reversed()
.thenComparing(type -> type.getKind() == ElementKind.INTERFACE ? 1 : 0)
.thenComparing(type -> type.getQualifiedName().toString());
}
/**
* Generates an instanceof if or if-else statement that checks the type and logs the element using
* the logger for the given type, if they're compatible.
*
* @param type the type to generate the check for
* @param element the element to be logged
* @param keyword either "if" or " else if"
* @param varName the name of the variable in the "instanceof" check
* @return the if or else-if statement
*/
private String generateIf(TypeElement type, Element element, String keyword, String varName) {
String ref = type.getQualifiedName().toString().replace('.', '_');
String loggerCall = generateLoggerCall(element, type, ref);
return "%s (%s instanceof %s %s) {\n %s;\n}"
.formatted(keyword, varName, type.getQualifiedName(), ref, loggerCall);
}
private String generateLoggerCall(Element element, TypeElement type, String elementReference) {
return ("Epilogue.%s.tryUpdate(dataLogger.getSubLogger(\"%s\"), %s, "
+ "Epilogue.getConfig().errorHandler)")
.formatted(StringUtils.loggerFieldName(type), loggedName(element), elementReference);
}
/**
* Computes the minimum inheritance distance between two types; that is, how many "extends" or
* "implements" clauses are required to get from one to the other.
*
* @param toCheck the type to check
* @param base the base type to check against
* @return the inheritance distance
*/
private int inheritanceDistance(TypeMirror toCheck, TypeMirror base) {
var types = m_processingEnv.getTypeUtils();
if (types.isSameType(toCheck, base)) {
return 0;
}
int distance = 1;
var parent = toCheck;
TypeElement element = m_processingEnv.getElementUtils().getTypeElement(parent.toString());
while (!types.isSameType(parent, base)
&& element.getInterfaces().stream().noneMatch(i -> types.isSameType(i, base))) {
element = m_processingEnv.getElementUtils().getTypeElement(parent.toString());
if (parent.getKind() == TypeKind.NONE) {
// Interface inheritance, there is no superclass
break;
}
parent = element.getSuperclass();
// Handle cases of interface inheritance
distance =
1
+ element.getInterfaces().stream()
.mapToInt(iface -> inheritanceDistance(iface, base))
.min()
.orElse(distance);
}
return distance;
}
}

View File

@@ -7,23 +7,36 @@ package edu.wpi.first.epilogue.processor;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.ReturnTree;
import com.sun.source.util.SimpleTreeVisitor;
import com.sun.source.util.Trees;
import edu.wpi.first.epilogue.Logged;
import edu.wpi.first.epilogue.NotLogged;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic;
/** Generates logger class files for {@link Logged @Logged}-annotated classes. */
public class LoggerGenerator {
public static final Predicate<ExecutableElement> kIsBuiltInJavaMethod =
LoggerGenerator::isBuiltInJavaMethod;
private final ProcessingEnvironment m_processingEnv;
private final List<ElementHandler> m_handlers;
@@ -36,6 +49,19 @@ public class LoggerGenerator {
return e.getAnnotation(NotLogged.class) == null;
}
/**
* Checks if a method is a method declared in java.lang.Object that should not be logged.
*
* @param e the method to check
* @return true if the method is toString, hashCode, or clone; false otherwise
*/
private static boolean isBuiltInJavaMethod(ExecutableElement e) {
Name name = e.getSimpleName();
return name.contentEquals("toString")
|| name.contentEquals("hashCode")
|| name.contentEquals("clone");
}
/**
* Generates the logger class used to handle data objects of the given type. The generated logger
* class will subclass from {@link edu.wpi.first.epilogue.logging.ClassSpecificLogger} and
@@ -53,17 +79,23 @@ public class LoggerGenerator {
Predicate<Element> optedIn =
e -> !requireExplicitOptIn || e.getAnnotation(Logged.class) != null;
var fieldsToLog =
clazz.getEnclosedElements().stream()
.filter(e -> e instanceof VariableElement)
.map(e -> (VariableElement) e)
.filter(notSkipped)
.filter(optedIn)
.filter(e -> !e.getModifiers().contains(Modifier.STATIC))
.filter(this::isLoggable)
.toList();
List<VariableElement> fieldsToLog;
if (Objects.equals(clazz.getSuperclass().toString(), "java.lang.Record")) {
// Do not log record members - just use the accessor methods
fieldsToLog = List.of();
} else {
fieldsToLog =
clazz.getEnclosedElements().stream()
.filter(e -> e instanceof VariableElement)
.map(e -> (VariableElement) e)
.filter(notSkipped)
.filter(optedIn)
.filter(e -> !e.getModifiers().contains(Modifier.STATIC))
.filter(this::isLoggable)
.toList();
}
var methodsToLog =
List<ExecutableElement> methodsToLog =
clazz.getEnclosedElements().stream()
.filter(e -> e instanceof ExecutableElement)
.map(e -> (ExecutableElement) e)
@@ -73,9 +105,51 @@ public class LoggerGenerator {
.filter(e -> e.getModifiers().contains(Modifier.PUBLIC))
.filter(e -> e.getParameters().isEmpty())
.filter(e -> e.getReceiverType() != null)
.filter(kIsBuiltInJavaMethod.negate())
.filter(this::isLoggable)
.filter(e -> !isSimpleGetterMethodForLoggedField(e, fieldsToLog))
.toList();
// Validate no name collisions
Map<String, List<Element>> usedNames =
Stream.concat(fieldsToLog.stream(), methodsToLog.stream())
.reduce(
new HashMap<>(),
(map, element) -> {
String name = ElementHandler.loggedName(element);
map.computeIfAbsent(name, _k -> new ArrayList<>()).add(element);
return map;
},
(left, right) -> {
left.putAll(right);
return left;
});
usedNames.forEach(
(name, elements) -> {
if (elements.size() > 1) {
// Collisions!
for (Element conflictingElement : elements) {
String conflicts =
elements.stream()
.filter(e -> !e.equals(conflictingElement))
.map(e -> e.getEnclosingElement().getSimpleName() + "." + e)
.collect(Collectors.joining(", "));
m_processingEnv
.getMessager()
.printMessage(
Diagnostic.Kind.ERROR,
"[EPILOGUE] Conflicting name detected: \""
+ name
+ "\" is also used by "
+ conflicts,
conflictingElement);
}
}
});
writeLoggerFile(clazz.getQualifiedName().toString(), config, fieldsToLog, methodsToLog);
}
@@ -242,4 +316,55 @@ public class LoggerGenerator {
private boolean isLoggable(Element element) {
return m_handlers.stream().anyMatch(h -> h.isLoggable(element));
}
/**
* Checks if a method is a simple "getter" method for a field in the given list. Here, we define
* "getter" as a method with a single return statement that references the name of a field, with
* no other expressions. `getX() { return x; }` would be considered a "getter" method, while
* `getX() { return x.clone(); }` would not be. Note that the method name is irrelevant; only the
* method body is checked.
*
* @param ex the method to check
* @param fieldsToLog the fields that will already be logged
* @return true if the method is a simple "getter" method, false otherwise
*/
private boolean isSimpleGetterMethodForLoggedField(
ExecutableElement ex, List<VariableElement> fieldsToLog) {
var trees = Trees.instance(m_processingEnv);
var methodTree = trees.getTree(ex);
if (methodTree == null) {
// probably a record's synthetic reader method
return false;
}
if (methodTree.getBody() == null) {
// Abstract or native method, can't be determined to be a getter
return false;
}
var statements = methodTree.getBody().getStatements();
if (statements.size() != 1) {
// More complex than a simple `return m_field` statement
return false;
}
var statement = statements.get(0);
if (!(statement instanceof ReturnTree ret)) {
// Shouldn't get here, since we've already filtered for methods that return a value
// and with a single statement - that one statement should be the return
return false;
}
var returnExpression = ret.getExpression();
return returnExpression.accept(
new SimpleTreeVisitor<Boolean, Void>(false) {
@Override
public Boolean visitIdentifier(IdentifierTree identifier, Void unused) {
return fieldsToLog.stream()
.anyMatch(v -> v.getSimpleName().contentEquals(identifier.getName()));
}
},
null);
}
}

View File

@@ -5,6 +5,9 @@
package edu.wpi.first.epilogue.processor;
import edu.wpi.first.epilogue.Logged;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.lang.model.element.TypeElement;
public final class StringUtils {
@@ -59,6 +62,33 @@ public final class StringUtils {
return builder.toString();
}
/**
* Splits a camel-cased string like "fooBar" into individual words like ["foo", "Bar"].
*
* @param camelCasedString the camel-cased string to split
* @return the individual words in the input
*/
public static List<String> splitToWords(CharSequence camelCasedString) {
// Implementation from https://stackoverflow.com/a/2560017, refactored for readability
// Uppercase letter not followed by the first letter of the next word
// This allows for splitting "IOLayer" into "IO" and "Layer"
String penultimateUppercaseLetter = "(?<=[A-Z])(?=[A-Z][a-z])";
// Any character that's NOT an uppercase letter, immediately followed by an uppercase letter
// This allows for splitting "fooBar" into "foo" and "Bar", or "123Bang" into "123" and "Bang"
String lastNonUppercaseLetter = "(?<=[^A-Z])(?=[A-Z])";
// The final letter in a sequence, followed by a non-alpha character like a number or underscore
// This allows for splitting "foo123" into "foo" and "123"
String finalLetter = "(?<=[A-Za-z])(?=[^A-Za-z])";
String regex =
String.format("%s|%s|%s", penultimateUppercaseLetter, lastNonUppercaseLetter, finalLetter);
return Arrays.asList(camelCasedString.toString().split(regex));
}
/**
* Gets the name of the field used to hold a logger for data of the given type.
*
@@ -107,4 +137,31 @@ public final class StringUtils {
return loggerClassName;
}
/**
* Converts a camelCase element name to separate words, removing common field and method name
* prefixes like "m_" and "get".
*
* @param elementName the camelcased element name
* @return the name split into separate words and sanitized
*/
public static String toHumanName(String elementName) {
// Delete common field prefixes (k_name, m_name, s_name)
var sanitizedName = elementName.replaceFirst("^[msk]_", "");
// Drop leading "k" prefix from fields
// (though normally these should be static, and thus not logged)
if (sanitizedName.matches("^k[A-Z].*$")) {
sanitizedName = sanitizedName.substring(1);
}
// Drop leading "get" from accessor methods
if (sanitizedName.matches("^get[A-Z].*$")) {
sanitizedName = sanitizedName.substring(3);
}
return splitToWords(sanitizedName).stream()
.map(StringUtils::capitalize)
.collect(Collectors.joining(" "));
}
}

View File

@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import static edu.wpi.first.epilogue.processor.CompileTestOptions.kJavaVersionOptions;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -300,9 +301,9 @@ class AnnotationProcessorTest {
byte[] arr1; // Should be logged
byte[][] arr2; // Should not be logged
public byte getX() { return x; }
public byte[] getArr1() { return arr1; }
public byte[][] getArr2() { return arr2; }
public byte getX() { return 0; }
public byte[] getArr1() { return null; }
public byte[][] getArr2() { return null; }
}
""";
@@ -347,9 +348,9 @@ class AnnotationProcessorTest {
char[] arr1; // Should not be logged
char[][] arr2; // Should not be logged
public char getX() { return x; }
public char[] getArr1() { return arr1; }
public char[][] getArr2() { return arr2; }
public char getX() { return 'x'; }
public char[] getArr1() { return null; }
public char[][] getArr2() { return null; }
}
""";
@@ -392,9 +393,9 @@ class AnnotationProcessorTest {
short[] arr1; // Should not be logged
short[][] arr2; // Should not be logged
public short getX() { return x; }
public short[] getArr1() { return arr1; }
public short[][] getArr2() { return arr2; }
public short getX() { return 0; }
public short[] getArr1() { return null; }
public short[][] getArr2() { return null; }
}
""";
@@ -437,9 +438,9 @@ class AnnotationProcessorTest {
int[] arr1; // Should be logged
int[][] arr2; // Should not be logged
public int getX() { return x; }
public int[] getArr1() { return arr1; }
public int[][] getArr2() { return arr2; }
public int getX() { return 0; }
public int[] getArr1() { return null; }
public int[][] getArr2() { return null; }
}
""";
@@ -484,9 +485,9 @@ class AnnotationProcessorTest {
long[] arr1; // Should be logged
long[][] arr2; // Should not be logged
public long getX() { return x; }
public long[] getArr1() { return arr1; }
public long[][] getArr2() { return arr2; }
public long getX() { return 0; }
public long[] getArr1() { return null; }
public long[][] getArr2() { return null; }
}
""";
@@ -531,9 +532,9 @@ class AnnotationProcessorTest {
float[] arr1; // Should be logged
float[][] arr2; // Should not be logged
public float getX() { return x; }
public float[] getArr1() { return arr1; }
public float[][] getArr2() { return arr2; }
public float getX() { return 0; }
public float[] getArr1() { return null; }
public float[][] getArr2() { return null; }
}
""";
@@ -581,9 +582,9 @@ class AnnotationProcessorTest {
double[][] arr2; // Should not be logged
List<Double> list; // Should not be logged
public double getX() { return x; }
public double[] getArr1() { return arr1; }
public double[][] getArr2() { return arr2; }
public double getX() { return 0; }
public double[] getArr1() { return null; }
public double[][] getArr2() { return null; }
}
""";
@@ -630,9 +631,9 @@ class AnnotationProcessorTest {
boolean[][] arr2; // Should not be logged
List<Boolean> list; // Should not be logged
public boolean getX() { return x; }
public boolean[] getArr1() { return arr1; }
public boolean[][] getArr2() { return arr2; }
public boolean getX() { return false; }
public boolean[] getArr1() { return null; }
public boolean[][] getArr2() { return null; }
}
""";
@@ -680,9 +681,9 @@ class AnnotationProcessorTest {
String[][] arr2; // Should not be logged
List<String> list; // Should be logged
public String getX() { return x; }
public String[] getArr1() { return arr1; }
public String[][] getArr2() { return arr2; }
public String getX() { return null; }
public String[] getArr1() { return null; }
public String[][] getArr2() { return null; }
}
""";
@@ -739,9 +740,9 @@ class AnnotationProcessorTest {
Structable[][] arr2; // Should not be logged
List<Structable> list; // Should be logged
public Structable getX() { return x; }
public Structable[] getArr1() { return arr1; }
public Structable[][] getArr2() { return arr2; }
public Structable getX() { return null; }
public Structable[] getArr1() { return null; }
public Structable[][] getArr2() { return null; }
}
""";
@@ -910,6 +911,7 @@ class AnnotationProcessorTest {
Compilation compilation =
javac()
.withOptions(kJavaVersionOptions)
.withProcessors(new AnnotationProcessor())
.compile(JavaFileObjects.forSourceString("edu.wpi.first.epilogue.Example", source));
@@ -1055,6 +1057,239 @@ class AnnotationProcessorTest {
assertLoggerGenerates(source, expectedRootLogger);
}
@Test
void inheritanceOfLoggedTypes() {
String source =
"""
package edu.wpi.first.epilogue;
@Logged
interface IFace {}
@Logged
class Impl1 implements IFace {}
@Logged
class Impl2 implements IFace {}
@Logged
interface I {
int a();
}
@Logged
interface I2 extends I {
int x();
}
@Logged
interface I3 extends I {
int y();
}
@Logged
interface I4 extends I2, I3 {
int z();
}
@Logged
class ConcreteLogged implements I4 {
public int a() { return 0; }
public int x() { return 0; }
public int y() { return 0; }
public int z() { return 0; }
}
class ConcreteNotLogged implements I4 {
public int a() { return 0; }
public int x() { return 0; }
public int y() { return 0; }
public int z() { return 0; }
}
@Logged
public class Example {
IFace asInterface;
Impl1 firstImpl;
Impl2 secondImpl;
I complex;
}
""";
String expectedRootLogger =
"""
package edu.wpi.first.epilogue;
import edu.wpi.first.epilogue.Logged;
import edu.wpi.first.epilogue.Epilogue;
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
import edu.wpi.first.epilogue.logging.DataLogger;
public class ExampleLogger extends ClassSpecificLogger<Example> {
public ExampleLogger() {
super(Example.class);
}
@Override
public void update(DataLogger dataLogger, Example object) {
if (Epilogue.shouldLog(Logged.Importance.DEBUG)) {
var $$asInterface = object.asInterface;
if ($$asInterface instanceof edu.wpi.first.epilogue.Impl1 edu_wpi_first_epilogue_Impl1) {
Epilogue.impl1Logger.tryUpdate(dataLogger.getSubLogger("asInterface"), edu_wpi_first_epilogue_Impl1, Epilogue.getConfig().errorHandler);
} else if ($$asInterface instanceof edu.wpi.first.epilogue.Impl2 edu_wpi_first_epilogue_Impl2) {
Epilogue.impl2Logger.tryUpdate(dataLogger.getSubLogger("asInterface"), edu_wpi_first_epilogue_Impl2, Epilogue.getConfig().errorHandler);
} else {
// Base type edu.wpi.first.epilogue.IFace
Epilogue.iFaceLogger.tryUpdate(dataLogger.getSubLogger("asInterface"), $$asInterface, Epilogue.getConfig().errorHandler);
};
Epilogue.impl1Logger.tryUpdate(dataLogger.getSubLogger("firstImpl"), object.firstImpl, Epilogue.getConfig().errorHandler);
Epilogue.impl2Logger.tryUpdate(dataLogger.getSubLogger("secondImpl"), object.secondImpl, Epilogue.getConfig().errorHandler);
var $$complex = object.complex;
if ($$complex instanceof edu.wpi.first.epilogue.ConcreteLogged edu_wpi_first_epilogue_ConcreteLogged) {
Epilogue.concreteLoggedLogger.tryUpdate(dataLogger.getSubLogger("complex"), edu_wpi_first_epilogue_ConcreteLogged, Epilogue.getConfig().errorHandler);
} else if ($$complex instanceof edu.wpi.first.epilogue.I4 edu_wpi_first_epilogue_I4) {
Epilogue.i4Logger.tryUpdate(dataLogger.getSubLogger("complex"), edu_wpi_first_epilogue_I4, Epilogue.getConfig().errorHandler);
} else if ($$complex instanceof edu.wpi.first.epilogue.I2 edu_wpi_first_epilogue_I2) {
Epilogue.i2Logger.tryUpdate(dataLogger.getSubLogger("complex"), edu_wpi_first_epilogue_I2, Epilogue.getConfig().errorHandler);
} else if ($$complex instanceof edu.wpi.first.epilogue.I3 edu_wpi_first_epilogue_I3) {
Epilogue.i3Logger.tryUpdate(dataLogger.getSubLogger("complex"), edu_wpi_first_epilogue_I3, Epilogue.getConfig().errorHandler);
} else {
// Base type edu.wpi.first.epilogue.I
Epilogue.iLogger.tryUpdate(dataLogger.getSubLogger("complex"), $$complex, Epilogue.getConfig().errorHandler);
};
}
}
}
""";
assertLoggerGenerates(source, expectedRootLogger);
}
@Test
void diamondInheritance() {
String source =
"""
package edu.wpi.first.epilogue;
@Logged
interface I {}
@Logged
interface ExtendingInterface extends I {}
@Logged
class Base implements I {}
/* Not @Logged */
// Diamond inheritance from I (I -> ExtendingInterface -> Inheritor, I -> Base -> Inheritor)
class Inheritor extends Base implements ExtendingInterface {}
@Logged
class Example {
// If this is set to an `Inheritor` instance, it will be logged as a `Base` object rather
// than `ExtendingInterface` or `I`
I theField;
}
""";
String expectedRootLogger =
"""
package edu.wpi.first.epilogue;
import edu.wpi.first.epilogue.Logged;
import edu.wpi.first.epilogue.Epilogue;
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
import edu.wpi.first.epilogue.logging.DataLogger;
public class ExampleLogger extends ClassSpecificLogger<Example> {
public ExampleLogger() {
super(Example.class);
}
@Override
public void update(DataLogger dataLogger, Example object) {
if (Epilogue.shouldLog(Logged.Importance.DEBUG)) {
var $$theField = object.theField;
if ($$theField instanceof edu.wpi.first.epilogue.Base edu_wpi_first_epilogue_Base) {
Epilogue.baseLogger.tryUpdate(dataLogger.getSubLogger("theField"), edu_wpi_first_epilogue_Base, Epilogue.getConfig().errorHandler);
} else if ($$theField instanceof edu.wpi.first.epilogue.ExtendingInterface edu_wpi_first_epilogue_ExtendingInterface) {
Epilogue.extendingInterfaceLogger.tryUpdate(dataLogger.getSubLogger("theField"), edu_wpi_first_epilogue_ExtendingInterface, Epilogue.getConfig().errorHandler);
} else {
// Base type edu.wpi.first.epilogue.I
Epilogue.iLogger.tryUpdate(dataLogger.getSubLogger("theField"), $$theField, Epilogue.getConfig().errorHandler);
};
}
}
}
""";
assertLoggerGenerates(source, expectedRootLogger);
}
@Test
void instanceofChainWithField() {
String source =
"""
package edu.wpi.first.epilogue;
@Logged
interface I {}
@Logged
class Base implements I {}
@Logged
class Example {
private I theField;
}
""";
String expectedRootLogger =
"""
package edu.wpi.first.epilogue;
import edu.wpi.first.epilogue.Logged;
import edu.wpi.first.epilogue.Epilogue;
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
import edu.wpi.first.epilogue.logging.DataLogger;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
public class ExampleLogger extends ClassSpecificLogger<Example> {
private static final VarHandle $theField;
static {
try {
var lookup = MethodHandles.privateLookupIn(Example.class, MethodHandles.lookup());
$theField = lookup.findVarHandle(Example.class, "theField", edu.wpi.first.epilogue.I.class);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("[EPILOGUE] Could not load private fields for logging!", e);
}
}
public ExampleLogger() {
super(Example.class);
}
@Override
public void update(DataLogger dataLogger, Example object) {
if (Epilogue.shouldLog(Logged.Importance.DEBUG)) {
var $$theField = (edu.wpi.first.epilogue.I) $theField.get(object);
if ($$theField instanceof edu.wpi.first.epilogue.Base edu_wpi_first_epilogue_Base) {
Epilogue.baseLogger.tryUpdate(dataLogger.getSubLogger("theField"), edu_wpi_first_epilogue_Base, Epilogue.getConfig().errorHandler);
} else {
// Base type edu.wpi.first.epilogue.I
Epilogue.iLogger.tryUpdate(dataLogger.getSubLogger("theField"), $$theField, Epilogue.getConfig().errorHandler);
};
}
}
}
""";
assertLoggerGenerates(source, expectedRootLogger);
}
@Test
void customLogger() {
String source =
@@ -1123,6 +1358,7 @@ class AnnotationProcessorTest {
Compilation compilation =
javac()
.withOptions(kJavaVersionOptions)
.withProcessors(new AnnotationProcessor())
.compile(JavaFileObjects.forSourceString("edu.wpi.first.epilogue.Example", source));
@@ -1135,6 +1371,187 @@ class AnnotationProcessorTest {
message);
}
@Test
void loggingRecords() {
String source =
"""
package edu.wpi.first.epilogue;
@Logged
record Example(double x, double y) { }
""";
String expectedRootLogger =
"""
package edu.wpi.first.epilogue;
import edu.wpi.first.epilogue.Logged;
import edu.wpi.first.epilogue.Epilogue;
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
import edu.wpi.first.epilogue.logging.DataLogger;
public class ExampleLogger extends ClassSpecificLogger<Example> {
public ExampleLogger() {
super(Example.class);
}
@Override
public void update(DataLogger dataLogger, Example object) {
if (Epilogue.shouldLog(Logged.Importance.DEBUG)) {
dataLogger.log("x", object.x());
dataLogger.log("y", object.y());
}
}
}
""";
assertLoggerGenerates(source, expectedRootLogger);
}
@Test
void errorsOnFieldNameConflicts() {
String source =
"""
package edu.wpi.first.epilogue;
@Logged
class Example {
@Logged(name = "Custom Name") double x;
@Logged(name = "Custom Name") double y;
@Logged(name = "Custom Name") double z;
}
""";
Compilation compilation =
javac()
.withProcessors(new AnnotationProcessor())
.compile(JavaFileObjects.forSourceString("edu.wpi.first.epilogue.Example", source));
assertThat(compilation).failed();
assertThat(compilation).hadErrorCount(3);
List<Diagnostic<? extends JavaFileObject>> errors = compilation.errors();
assertAll(
() ->
assertCompilationError(
"[EPILOGUE] Conflicting name detected: \"Custom Name\" is also used by Example.y, Example.z",
5,
40,
errors.get(0)),
() ->
assertCompilationError(
"[EPILOGUE] Conflicting name detected: \"Custom Name\" is also used by Example.x, Example.z",
6,
40,
errors.get(1)),
() ->
assertCompilationError(
"[EPILOGUE] Conflicting name detected: \"Custom Name\" is also used by Example.x, Example.y",
7,
40,
errors.get(2)));
}
@Test
void doesNotErrorOnGetterMethod() {
String source =
"""
package edu.wpi.first.epilogue;
@Logged
class Example {
double x;
public double x() { return x; }
public double getX() { return x; }
public double aTotallyArbitraryNameForAnAccessorMethod() { return x; }
public double withANoOpTransform() { return x + 0; }
public double withTemp() { var temp = x; return temp; }
}
""";
String expectedRootLogger =
"""
package edu.wpi.first.epilogue;
import edu.wpi.first.epilogue.Logged;
import edu.wpi.first.epilogue.Epilogue;
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
import edu.wpi.first.epilogue.logging.DataLogger;
public class ExampleLogger extends ClassSpecificLogger<Example> {
public ExampleLogger() {
super(Example.class);
}
@Override
public void update(DataLogger dataLogger, Example object) {
if (Epilogue.shouldLog(Logged.Importance.DEBUG)) {
dataLogger.log("x", object.x);
dataLogger.log("withANoOpTransform", object.withANoOpTransform());
dataLogger.log("withTemp", object.withTemp());
}
}
}
""";
assertLoggerGenerates(source, expectedRootLogger);
}
@Test
void configuredDefaultNaming() {
String source =
"""
package edu.wpi.first.epilogue;
@Logged(defaultNaming = Logged.Naming.USE_HUMAN_NAME)
class Example {
double m_memberPrefix;
double kConstantPrefix;
double k_otherConstantPrefix;
double s_otherPrefix;
public double getTheGetterMethod() {
return 0;
}
@Logged(defaultNaming = Logged.Naming.USE_CODE_NAME)
public double optedOut() {
return 0;
}
}
""";
String expectedRootLogger =
"""
package edu.wpi.first.epilogue;
import edu.wpi.first.epilogue.Logged;
import edu.wpi.first.epilogue.Epilogue;
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
import edu.wpi.first.epilogue.logging.DataLogger;
public class ExampleLogger extends ClassSpecificLogger<Example> {
public ExampleLogger() {
super(Example.class);
}
@Override
public void update(DataLogger dataLogger, Example object) {
if (Epilogue.shouldLog(Logged.Importance.DEBUG)) {
dataLogger.log("Member Prefix", object.m_memberPrefix);
dataLogger.log("Constant Prefix", object.kConstantPrefix);
dataLogger.log("Other Constant Prefix", object.k_otherConstantPrefix);
dataLogger.log("Other Prefix", object.s_otherPrefix);
dataLogger.log("The Getter Method", object.getTheGetterMethod());
dataLogger.log("optedOut", object.optedOut());
}
}
}
""";
assertLoggerGenerates(source, expectedRootLogger);
}
private void assertCompilationError(
String message, long lineNumber, long col, Diagnostic<? extends JavaFileObject> diagnostic) {
assertAll(
@@ -1149,6 +1566,7 @@ class AnnotationProcessorTest {
private void assertLoggerGenerates(String loggedClassContent, String loggerClassContent) {
Compilation compilation =
javac()
.withOptions(kJavaVersionOptions)
.withProcessors(new AnnotationProcessor())
.compile(
JavaFileObjects.forSourceString(

View File

@@ -0,0 +1,13 @@
// 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.epilogue.processor;
import java.util.List;
public class CompileTestOptions {
public static final int kJavaVersion = 17;
public static final List<Object> kJavaVersionOptions =
List.of("-source", kJavaVersion, "-target", kJavaVersion);
}

View File

@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import static edu.wpi.first.epilogue.processor.CompileTestOptions.kJavaVersionOptions;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.google.testing.compile.Compilation;
@@ -171,7 +172,7 @@ class EpilogueGeneratorTest {
config.loggingPeriod = Seconds.of(robot.getPeriod());
}
if (config.loggingPeriodOffset == null) {
config.loggingPeriodOffset = config.loggingPeriod.divide(2);
config.loggingPeriodOffset = config.loggingPeriod.div(2);
}
robot.addPeriodic(() -> {
@@ -251,7 +252,7 @@ class EpilogueGeneratorTest {
config.loggingPeriod = Seconds.of(robot.getPeriod());
}
if (config.loggingPeriodOffset == null) {
config.loggingPeriodOffset = config.loggingPeriod.divide(2);
config.loggingPeriodOffset = config.loggingPeriod.div(2);
}
robot.addPeriodic(() -> {
@@ -283,7 +284,7 @@ class EpilogueGeneratorTest {
config.loggingPeriod = Seconds.of(robot.getPeriod());
}
if (config.loggingPeriodOffset == null) {
config.loggingPeriodOffset = config.loggingPeriod.divide(2);
config.loggingPeriodOffset = config.loggingPeriod.div(2);
}
robot.addPeriodic(() -> {
@@ -363,6 +364,7 @@ class EpilogueGeneratorTest {
String loggedClassContent, String loggerClassContent) {
Compilation compilation =
javac()
.withOptions(kJavaVersionOptions)
.withProcessors(new AnnotationProcessor())
.compile(JavaFileObjects.forSourceString("", loggedClassContent));

View File

@@ -4,8 +4,10 @@
package edu.wpi.first.epilogue.processor;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
import org.junit.jupiter.api.Test;
class StringUtilsTest {
@@ -16,4 +18,22 @@ class StringUtilsTest {
assertEquals("fooBar", StringUtils.lowerCamelCase("FooBar"));
assertEquals("allcaps", StringUtils.lowerCamelCase("ALLCAPS"));
}
@Test
void splitToWords() {
assertAll(
() -> assertEquals(List.of("IO", "Logger"), StringUtils.splitToWords("IOLogger")),
() -> assertEquals(List.of("LED", "Subsystem"), StringUtils.splitToWords("LEDSubsystem")),
() -> assertEquals(List.of("Foo", "Bar"), StringUtils.splitToWords("FooBar")),
() -> assertEquals(List.of("ALLCAPS"), StringUtils.splitToWords("ALLCAPS")),
() ->
assertEquals(List.of("k", "First", "Second"), StringUtils.splitToWords("kFirstSecond")),
() ->
assertEquals(
List.of("there", "Is", "A", "Number", "123", "In", "Here", "VERSION", "456"),
StringUtils.splitToWords("thereIsANumber123InHereVERSION456")),
() ->
assertEquals(
List.of("get", "First", "Second"), StringUtils.splitToWords("getFirstSecond")));
}
}

View File

@@ -89,4 +89,39 @@ public @interface Logged {
* @return the importance of the annotated element
*/
Importance importance() default Importance.DEBUG;
/**
* Different behaviors for how Epilogue will generate the names of logged data points. This only
* applies to automatically generated names; any specific name provided with {@link #name()} will
* take precedence over an automatically generated name.
*/
enum Naming {
/**
* Sets the default naming strategy to use the name of the element as it appears in source code.
* For example, a field {@code double m_x} would be labeled as {@code "m_x"} by default, and a
* {@code getX()} accessor would be labeled as {@code "getX"}.
*/
USE_CODE_NAME,
/**
* Sets the default naming strategy to use a human-readable name based on the name of the name
* of the element as it appears in source code. For example, a field {@code double m_x} would be
* labeled as {@code "X"} by default, and a {@code getX()} accessor would also be labeled as
* {@code "X"}. Because logged names must be unique, this configuration would fail to compile
* and require either one of the fields to be excluded from logs (which, for simple accessors,
* would be ideal to avoid duplicate data), or to rename one or both elements so the logged data
* fields would have unique names.
*/
USE_HUMAN_NAME
}
/**
* The default naming behavior to use. Defaults to {@link Naming#USE_CODE_NAME}, which uses the
* raw code name directly in logs. Any configuration of the {@link #name()} attribute on logged
* fields and methods will take precedence over an automatically generated name.
*
* @return the naming strategy for and annotated field or method, or the default naming strategy
* for all logged fields and methods in an annotated class
*/
Naming defaultNaming() default Naming.USE_CODE_NAME;
}

View File

@@ -69,19 +69,6 @@ tasks.withType(CppCompile) {
nativeUtils.exportsConfigs {
glass {
x64ExcludeSymbols = [
'_CT??_R0?AV_System_error',
'_CT??_R0?AVexception',
'_CT??_R0?AVfailure',
'_CT??_R0?AVruntime_error',
'_CT??_R0?AVsystem_error',
'_CTA5?AVfailure',
'_TI5?AVfailure',
'_CT??_R0?AVout_of_range',
'_CTA3?AVout_of_range',
'_TI3?AVout_of_range',
'_CT??_R0?AVbad_cast'
]
}
}

View File

@@ -38,7 +38,7 @@ static void WorkspaceResetImpl() {
// clear storage
for (auto&& root : gContext->storageRoots) {
root.second->Clear();
root.second.Clear();
}
// ImGui reset
@@ -53,7 +53,7 @@ static void WorkspaceInit() {
}
for (auto&& root : gContext->storageRoots) {
root.getValue()->Apply();
root.second.Apply();
}
}
@@ -152,19 +152,14 @@ static bool LoadStorageRootImpl(Context* ctx, const std::string& filename,
fileBuffer.error().message().c_str());
return false;
}
auto& storage = ctx->storageRoots[rootName];
bool createdStorage = false;
if (!storage) {
storage = std::make_unique<Storage>();
createdStorage = true;
}
auto [it, createdStorage] = ctx->storageRoots.try_emplace(rootName);
try {
storage->FromJson(wpi::json::parse(fileBuffer.value()->GetCharBuffer()),
filename.c_str());
it->second.FromJson(wpi::json::parse(fileBuffer.value()->GetCharBuffer()),
filename.c_str());
} catch (wpi::json::parse_error& e) {
ImGui::LogText("Error loading %s: %s", filename.c_str(), e.what());
if (createdStorage) {
ctx->storageRoots.erase(rootName);
ctx->storageRoots.erase(it);
}
return false;
}
@@ -176,7 +171,7 @@ static bool LoadStorageImpl(Context* ctx, std::string_view dir,
bool rv = true;
for (auto&& root : ctx->storageRoots) {
std::string filename;
auto rootName = root.getKey();
auto& rootName = root.first;
if (rootName.empty()) {
filename = (fs::path{dir} / fmt::format("{}.json", name)).string();
} else {
@@ -291,7 +286,7 @@ static bool SaveStorageImpl(Context* ctx, std::string_view dir,
if (exiting && wpi::gui::gContext->resetOnExit) {
fs::remove(dirPath / fmt::format("{}-window.json", name), ec);
for (auto&& root : ctx->storageRoots) {
auto rootName = root.getKey();
auto& rootName = root.first;
if (rootName.empty()) {
fs::remove(dirPath / fmt::format("{}.json", name), ec);
} else {
@@ -304,14 +299,14 @@ static bool SaveStorageImpl(Context* ctx, std::string_view dir,
(dirPath / fmt::format("{}-window.json", name)).string());
for (auto&& root : ctx->storageRoots) {
auto rootName = root.getKey();
auto& rootName = root.first;
std::string filename;
if (rootName.empty()) {
filename = (dirPath / fmt::format("{}.json", name)).string();
} else {
filename = (dirPath / fmt::format("{}-{}.json", name, rootName)).string();
}
if (!SaveStorageRootImpl(ctx, filename, *root.getValue())) {
if (!SaveStorageRootImpl(ctx, filename, root.second)) {
rv = false;
}
}
@@ -319,10 +314,9 @@ static bool SaveStorageImpl(Context* ctx, std::string_view dir,
}
Context::Context()
: sourceNameStorage{storageRoots.insert({"", std::make_unique<Storage>()})
.first->getValue()
->GetChild("sourceNames")} {
storageStack.emplace_back(storageRoots[""].get());
: sourceNameStorage{
storageRoots.try_emplace("").first->second.GetChild("sourceNames")} {
storageStack.emplace_back(&storageRoots[""]);
// override ImGui ini saving
wpi::gui::ConfigureCustomSaveSettings(
@@ -435,11 +429,7 @@ Storage& glass::GetCurStorageRoot() {
}
Storage& glass::GetStorageRoot(std::string_view rootName) {
auto& storage = gContext->storageRoots[rootName];
if (!storage) {
storage = std::make_unique<Storage>();
}
return *storage;
return gContext->storageRoots[rootName];
}
void glass::ResetStorageStack(std::string_view rootName) {

View File

@@ -112,5 +112,5 @@ DataSource* DataSource::Find(std::string_view id) {
if (it == gContext->sources.end()) {
return nullptr;
}
return it->getValue();
return it->second;
}

View File

@@ -331,7 +331,7 @@ std::vector<std::unique_ptr<Storage>>& Storage::GetChildArray(
std::unique_ptr<Storage::Value> Storage::Erase(std::string_view key) {
auto it = m_values.find(key);
if (it != m_values.end()) {
auto rv = std::move(it->getValue());
auto rv = std::move(it->second);
m_values.erase(it);
return rv;
}
@@ -339,11 +339,9 @@ std::unique_ptr<Storage::Value> Storage::Erase(std::string_view key) {
}
void Storage::EraseChildren() {
for (auto&& kv : m_values) {
if (kv.getValue()->type == Value::kChild) {
m_values.remove(&kv);
}
}
std::erase_if(m_values, [](const auto& kv) {
return kv.second->type == Value::kChild;
});
}
static bool JsonArrayToStorage(Storage::Value* valuePtr, const wpi::json& jarr,
@@ -559,7 +557,7 @@ wpi::json Storage::ToJson() const {
wpi::json j = wpi::json::object();
for (auto&& kv : m_values) {
wpi::json jelem;
auto& value = *kv.getValue();
auto& value = *kv.second;
switch (value.type) {
#define CASE(CapsName, LowerName) \
case Value::k##CapsName: \
@@ -602,7 +600,7 @@ wpi::json Storage::ToJson() const {
default:
continue;
}
j.emplace(kv.getKey(), std::move(jelem));
j.emplace(kv.first, std::move(jelem));
}
return j;
}
@@ -617,7 +615,7 @@ void Storage::Clear() {
void Storage::ClearValues() {
for (auto&& kv : m_values) {
auto& value = *kv.getValue();
auto& value = *kv.second;
switch (value.type) {
case Value::kInt:
value.intVal = value.intDefault;
@@ -703,7 +701,7 @@ void Storage::Apply() {
void Storage::ApplyChildren() {
for (auto&& kv : m_values) {
auto& value = *kv.getValue();
auto& value = *kv.second;
switch (value.type) {
case Value::kChild:
value.child->Apply();

View File

@@ -0,0 +1,32 @@
// 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/Alerts.h"
#include <IconsFontAwesome6.h>
#include <imgui.h>
using namespace glass;
void glass::DisplayAlerts(AlertsModel* model) {
auto& infos = model->GetInfos();
auto& warnings = model->GetWarnings();
auto& errors = model->GetErrors();
const ImVec4 kWarningColor{0.85f, 0.56f, 0.0f, 1.0f};
const ImVec4 kErrorColor{0.9f, 0.25f, 0.25f, 1.0f};
// show higher severity alerts on top
for (auto&& error : errors) {
ImGui::TextColored(kErrorColor, "%s %s", ICON_FA_CIRCLE_XMARK,
error.c_str());
}
for (auto&& warning : warnings) {
ImGui::TextColored(kWarningColor, "%s %s", ICON_FA_TRIANGLE_EXCLAMATION,
warning.c_str());
}
for (auto&& info : infos) {
ImGui::Text("%s %s", ICON_FA_CIRCLE_INFO, info.c_str());
}
}

View File

@@ -235,7 +235,7 @@ class FieldInfo {
FieldFrameData GetFrameData(ImVec2 min, ImVec2 max) const;
void Draw(ImDrawList* drawList, const FieldFrameData& frameData) const;
wpi::StringMap<std::unique_ptr<ObjectInfo>> m_objects;
wpi::StringMap<ObjectInfo> m_objects;
private:
void Reset();
@@ -591,6 +591,12 @@ FieldFrameData FieldInfo::GetFrameData(ImVec2 min, ImVec2 max) const {
max.x -= 20;
min.y += 20;
max.y -= 20;
// also pad the image so it's the same size as the box
ffd.imageMin.x += 20;
ffd.imageMax.x -= 20;
ffd.imageMin.y += 20;
ffd.imageMax.y -= 20;
}
ffd.min = min;
@@ -946,15 +952,12 @@ void glass::DisplayField2DSettings(Field2DModel* model) {
return;
}
PushID(name);
auto& objRef = field->m_objects[name];
if (!objRef) {
objRef = std::make_unique<ObjectInfo>(GetStorage());
}
auto obj = objRef.get();
wpi::SmallString<64> nameBuf{name};
if (ImGui::CollapsingHeader(nameBuf.c_str())) {
obj->DisplaySettings();
auto& obj =
field->m_objects.try_emplace(name, GetStorage()).first->second;
obj.DisplaySettings();
}
PopID();
});
@@ -1090,14 +1093,10 @@ void FieldDisplay::Display(FieldInfo* field, Field2DModel* model,
void FieldDisplay::DisplayObject(FieldObjectModel& model,
std::string_view name) {
PushID(name);
auto& objRef = m_field->m_objects[name];
if (!objRef) {
objRef = std::make_unique<ObjectInfo>(GetStorage());
}
auto obj = objRef.get();
obj->LoadImage();
auto& obj = m_field->m_objects.try_emplace(name, GetStorage()).first->second;
obj.LoadImage();
auto displayOptions = obj->GetDisplayOptions();
auto displayOptions = obj.GetDisplayOptions();
m_centerLine.resize(0);
m_leftLine.resize(0);
@@ -1134,9 +1133,9 @@ void FieldDisplay::DisplayObject(FieldObjectModel& model,
}
m_drawSplit.SetCurrentChannel(m_drawList, 0);
obj->DrawLine(m_drawList, m_centerLine);
obj->DrawLine(m_drawList, m_leftLine);
obj->DrawLine(m_drawList, m_rightLine);
obj.DrawLine(m_drawList, m_centerLine);
obj.DrawLine(m_drawList, m_leftLine);
obj.DrawLine(m_drawList, m_rightLine);
m_drawSplit.Merge(m_drawList);
PopID();

View File

@@ -7,7 +7,6 @@
#include <stdint.h>
#include <functional>
#include <memory>
#include <string>
#include <vector>
@@ -34,7 +33,7 @@ class Context {
std::string storageAutoSaveDir = ".";
std::string storageName = "imgui";
wpi::SmallVector<Storage*, 32> storageStack;
wpi::StringMap<std::unique_ptr<Storage>> storageRoots;
wpi::StringMap<Storage> storageRoots;
wpi::StringMap<bool> deviceHidden;
wpi::StringMap<DataSource*> sources;
Storage& sourceNameStorage;

View File

@@ -62,7 +62,10 @@ class Provider : public WindowManager {
* Perform global initialization. This should be called prior to
* wpi::gui::Initialize().
*/
void GlobalInit() override;
void GlobalInit() override {
WindowManager::GlobalInit();
wpi::gui::AddEarlyExecute([this] { Update(); });
}
/**
* Show the specified view by default on first load. Has no effect if
@@ -70,7 +73,17 @@ class Provider : public WindowManager {
*
* @param name View name
*/
void ShowDefault(std::string_view name);
void ShowDefault(std::string_view name) {
auto win = GetWindow(name);
if (win) {
return;
}
auto it = FindViewEntry(name);
if (it == m_viewEntries.end() || (*it)->name != name) {
return;
}
(*it)->showDefault = true;
}
/**
* Register a model and view combination. Equivalent to calling both
@@ -82,7 +95,10 @@ class Provider : public WindowManager {
* @param createView Functor for creating view
*/
void Register(std::string_view name, ExistsFunc exists,
CreateModelFunc createModel, CreateViewFunc createView);
CreateModelFunc createModel, CreateViewFunc createView) {
RegisterModel(name, std::move(exists), std::move(createModel));
RegisterView(name, name, nullptr, std::move(createView));
}
/**
* Register a model.
@@ -92,7 +108,16 @@ class Provider : public WindowManager {
* @param createModel Functor for creating model
*/
void RegisterModel(std::string_view name, ExistsFunc exists,
CreateModelFunc createModel);
CreateModelFunc createModel) {
auto it = FindModelEntry(name);
// ignore if exists
if (it != m_modelEntries.end() && (*it)->name == name) {
return;
}
// insert in sorted location
m_modelEntries.emplace(
it, MakeModelEntry(name, std::move(exists), std::move(createModel)));
}
/**
* Register a view.
@@ -103,10 +128,33 @@ class Provider : public WindowManager {
* @param createView Functor for creating view
*/
void RegisterView(std::string_view name, std::string_view modelName,
ViewExistsFunc exists, CreateViewFunc createView);
ViewExistsFunc exists, CreateViewFunc createView) {
// find model; if model doesn't exist, ignore
auto modelIt = FindModelEntry(modelName);
if (modelIt == m_modelEntries.end() || (*modelIt)->name != modelName) {
return;
}
auto viewIt = FindViewEntry(name);
// ignore if exists
if (viewIt != m_viewEntries.end() && (*viewIt)->name == name) {
return;
}
// insert in sorted location
m_viewEntries.emplace(viewIt,
MakeViewEntry(name, modelIt->get(), std::move(exists),
std::move(createView)));
}
protected:
virtual void Update();
virtual void Update() {
// update entries
for (auto&& entry : m_modelEntries) {
if (entry->model) {
entry->model->Update();
}
}
}
struct ModelEntry {
ModelEntry(std::string_view name, ExistsFunc exists,
@@ -145,8 +193,17 @@ class Provider : public WindowManager {
using ViewEntries = std::vector<std::unique_ptr<ViewEntry>>;
ViewEntries m_viewEntries;
typename ModelEntries::iterator FindModelEntry(std::string_view name);
typename ViewEntries::iterator FindViewEntry(std::string_view name);
typename ModelEntries::iterator FindModelEntry(std::string_view name) {
return std::lower_bound(
m_modelEntries.begin(), m_modelEntries.end(), name,
[](const auto& elem, std::string_view s) { return elem->name < s; });
}
typename ViewEntries::iterator FindViewEntry(std::string_view name) {
return std::lower_bound(
m_viewEntries.begin(), m_viewEntries.end(), name,
[](const auto& elem, std::string_view s) { return elem->name < s; });
}
virtual std::unique_ptr<ModelEntry> MakeModelEntry(
std::string_view name, ExistsFunc exists, CreateModelFunc createModel) {
@@ -166,5 +223,3 @@ class Provider : public WindowManager {
};
} // namespace glass
#include "Provider.inc"

View File

@@ -1,102 +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 <utility>
#include "glass/Provider.h"
namespace glass {
template <typename Functions>
void Provider<Functions>::GlobalInit() {
WindowManager::GlobalInit();
wpi::gui::AddEarlyExecute([this] { Update(); });
}
template <typename Functions>
void Provider<Functions>::ShowDefault(std::string_view name) {
auto win = GetWindow(name);
if (win) {
return;
}
auto it = FindViewEntry(name);
if (it == m_viewEntries.end() || (*it)->name != name) {
return;
}
(*it)->showDefault = true;
}
template <typename Functions>
void Provider<Functions>::Register(std::string_view name, ExistsFunc exists,
CreateModelFunc createModel,
CreateViewFunc createView) {
RegisterModel(name, std::move(exists), std::move(createModel));
RegisterView(name, name, nullptr, std::move(createView));
}
template <typename Functions>
void Provider<Functions>::RegisterModel(std::string_view name,
ExistsFunc exists,
CreateModelFunc createModel) {
auto it = FindModelEntry(name);
// ignore if exists
if (it != m_modelEntries.end() && (*it)->name == name) {
return;
}
// insert in sorted location
m_modelEntries.emplace(
it, MakeModelEntry(name, std::move(exists), std::move(createModel)));
}
template <typename Functions>
void Provider<Functions>::RegisterView(std::string_view name,
std::string_view modelName,
ViewExistsFunc exists,
CreateViewFunc createView) {
// find model; if model doesn't exist, ignore
auto modelIt = FindModelEntry(modelName);
if (modelIt == m_modelEntries.end() || (*modelIt)->name != modelName) {
return;
}
auto viewIt = FindViewEntry(name);
// ignore if exists
if (viewIt != m_viewEntries.end() && (*viewIt)->name == name) {
return;
}
// insert in sorted location
m_viewEntries.emplace(viewIt,
MakeViewEntry(name, modelIt->get(), std::move(exists),
std::move(createView)));
}
template <typename Functions>
void Provider<Functions>::Update() {
// update entries
for (auto&& entry : m_modelEntries) {
if (entry->model) {
entry->model->Update();
}
}
}
template <typename Functions>
typename Provider<Functions>::ModelEntries::iterator
Provider<Functions>::FindModelEntry(std::string_view name) {
return std::lower_bound(
m_modelEntries.begin(), m_modelEntries.end(), name,
[](const auto& elem, std::string_view s) { return elem->name < s; });
}
template <typename Functions>
typename Provider<Functions>::ViewEntries::iterator
Provider<Functions>::FindViewEntry(std::string_view name) {
return std::lower_bound(
m_viewEntries.begin(), m_viewEntries.end(), name,
[](const auto& elem, std::string_view s) { return elem->name < s; });
}
} // namespace glass

View File

@@ -249,8 +249,7 @@ class ChildIterator {
public:
ChildIterator(IteratorType it, IteratorType end) noexcept
: anchor(it), end(end) {
while (anchor != end &&
anchor->getValue()->type != Storage::Value::kChild) {
while (anchor != end && anchor->second->type != Storage::Value::kChild) {
++anchor;
}
}
@@ -261,8 +260,7 @@ class ChildIterator {
/// increment operator (needed for range-based for)
ChildIterator& operator++() {
++anchor;
while (anchor != end &&
anchor->getValue()->type != Storage::Value::kChild) {
while (anchor != end && anchor->second->type != Storage::Value::kChild) {
++anchor;
}
return *this;
@@ -274,10 +272,10 @@ class ChildIterator {
}
/// return key of the iterator
std::string_view key() const { return anchor->getKey(); }
std::string_view key() const { return anchor->first; }
/// return value of the iterator
Storage& value() const { return *anchor->getValue()->child; }
Storage& value() const { return *anchor->second->child; }
};
} // namespace detail

View File

@@ -0,0 +1,23 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <string>
#include <vector>
#include "glass/Model.h"
namespace glass {
class AlertsModel : public Model {
public:
virtual const std::vector<std::string>& GetInfos() = 0;
virtual const std::vector<std::string>& GetWarnings() = 0;
virtual const std::vector<std::string>& GetErrors() = 0;
};
void DisplayAlerts(AlertsModel* model);
} // namespace glass

View File

@@ -0,0 +1,51 @@
// 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/NTAlerts.h"
#include <utility>
#include <fmt/format.h>
using namespace glass;
NTAlertsModel::NTAlertsModel(std::string_view path)
: NTAlertsModel{nt::NetworkTableInstance::GetDefault(), path} {}
NTAlertsModel::NTAlertsModel(nt::NetworkTableInstance inst,
std::string_view path)
: m_inst{inst},
m_infos{m_inst.GetStringArrayTopic(fmt::format("{}/infos", path))
.Subscribe({})},
m_warnings{m_inst.GetStringArrayTopic(fmt::format("{}/warnings", path))
.Subscribe({})},
m_errors{m_inst.GetStringArrayTopic(fmt::format("{}/errors", path))
.Subscribe({})} {}
void NTAlertsModel::Update() {
if (!m_infos.Exists()) {
m_infosValue.clear();
}
for (auto&& v : m_infos.ReadQueue()) {
m_infosValue = std::move(v.value);
}
if (!m_warnings.Exists()) {
m_warningsValue.clear();
}
for (auto&& v : m_warnings.ReadQueue()) {
m_warningsValue = std::move(v.value);
}
if (!m_errors.Exists()) {
m_errorsValue.clear();
}
for (auto&& v : m_errors.ReadQueue()) {
m_errorsValue = std::move(v.value);
}
}
bool NTAlertsModel::Exists() {
return m_infos.Exists();
}

View File

@@ -4,6 +4,7 @@
#include <memory>
#include "glass/networktables/NTAlerts.h"
#include "glass/networktables/NTCommandScheduler.h"
#include "glass/networktables/NTCommandSelector.h"
#include "glass/networktables/NTDifferentialDrive.h"
@@ -24,6 +25,16 @@
using namespace glass;
void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
provider.Register(
NTAlertsModel::kType,
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTAlertsModel>(inst, path);
},
[](Window* win, Model* model, const char*) {
win->SetDefaultSize(300, 150);
return MakeFunctionView(
[=] { DisplayAlerts(static_cast<NTAlertsModel*>(model)); });
});
provider.Register(
NTCommandSchedulerModel::kType,
[](nt::NetworkTableInstance inst, const char* path) {

View File

@@ -0,0 +1,49 @@
// 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>
#include <string_view>
#include <vector>
#include <networktables/NetworkTableInstance.h>
#include <networktables/StringArrayTopic.h>
#include "glass/other/Alerts.h"
namespace glass {
class NTAlertsModel : public AlertsModel {
public:
static constexpr const char* kType = "Alerts";
// path is to the table containing ".type", excluding the trailing /
explicit NTAlertsModel(std::string_view path);
NTAlertsModel(nt::NetworkTableInstance inst, std::string_view path);
const std::vector<std::string>& GetInfos() override { return m_infosValue; }
const std::vector<std::string>& GetWarnings() override {
return m_warningsValue;
}
const std::vector<std::string>& GetErrors() override { return m_errorsValue; }
void Update() override;
bool Exists() override;
bool IsReadOnly() override { return false; }
private:
nt::NetworkTableInstance m_inst;
nt::StringArraySubscriber m_infos;
nt::StringArraySubscriber m_warnings;
nt::StringArraySubscriber m_errors;
std::vector<std::string> m_infosValue;
std::vector<std::string> m_warningsValue;
std::vector<std::string> m_errorsValue;
};
} // namespace glass

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

7
gradlew vendored
View File

@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

22
gradlew.bat vendored
View File

@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail

91
hal/BUILD.bazel Normal file
View File

@@ -0,0 +1,91 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
cc_library(
name = "generated_cc_headers",
hdrs = glob(["src/generated/main/native/include/**"]),
includes = ["src/generated/main/native/include"],
strip_include_prefix = "src/generated/main/native/include",
visibility = ["//hal:__subpackages__"],
)
filegroup(
name = "generated_java",
srcs = glob(["src/generated/main/java/**/*.java"]),
visibility = ["//hal:__subpackages__"],
)
ATHENA_SRCS = glob(["src/main/native/athena/**"])
ATHENA_DEPS = ["@bzlmodrio-ni//libraries/cpp/ni:shared"]
SIM_SRCS = glob(["src/main/native/sim/**"])
SIM_DEPS = []
HAL_DEPS = select({
"@rules_bzlmodrio_toolchains//constraints/is_roborio:roborio": ATHENA_DEPS,
"//conditions:default": SIM_DEPS,
})
filegroup(
name = "platform-srcs",
srcs = select({
"@rules_bzlmodrio_toolchains//constraints/is_roborio:roborio": ATHENA_SRCS,
"//conditions:default": SIM_SRCS,
}),
)
cc_library(
name = "wpiHal.static",
srcs = [":platform-srcs"] + glob(
["src/main/native/cpp/**"],
exclude = ["src/main/native/cpp/jni/**"],
),
hdrs = glob(["src/main/native/include/**/*"]),
includes = ["src/main/native/include"],
strip_include_prefix = "src/main/native/include",
visibility = ["//visibility:public"],
deps = [
":generated_cc_headers",
"//wpiutil:wpiutil.static",
] + HAL_DEPS,
)
java_library(
name = "hal-java",
srcs = [":generated_java"] + glob(["src/main/java/**/*.java"]),
visibility = ["//visibility:public"],
deps = [
"//wpiutil:wpiutil-java",
],
)
cc_test(
name = "hal-cpp-test",
size = "small",
srcs = glob([
"src/test/native/**/*.cpp",
"src/test/native/**/*.h",
]),
deps = [
":wpiHal.static",
"//thirdparty/googletest:googletest.static",
],
)
cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
deps = [
":wpiHal.static",
],
)
java_binary(
name = "DevMain-Java",
srcs = ["src/dev/java/edu/wpi/first/hal/DevMain.java"],
main_class = "edu.wpi.first.hal.DevMain",
deps = [
],
)

View File

@@ -74,19 +74,6 @@ Action<List<String>> symbolFilter = { symbols ->
nativeUtils.exportsConfigs {
hal {
x64ExcludeSymbols = [
'_CT??_R0?AV_System_error',
'_CT??_R0?AVexception',
'_CT??_R0?AVfailure',
'_CT??_R0?AVruntime_error',
'_CT??_R0?AVsystem_error',
'_CTA5?AVfailure',
'_TI5?AVfailure',
'_CT??_R0?AVout_of_range',
'_CTA3?AVout_of_range',
'_TI3?AVout_of_range',
'_CT??_R0?AVbad_cast'
]
}
halJNI {
x64SymbolFilter = symbolFilter

View File

@@ -3,9 +3,9 @@
# 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.
from pathlib import Path
import sys
import argparse
from pathlib import Path
def generate_usage_reporting(output_directory: Path, template_directory: Path):
@@ -49,7 +49,7 @@ def generate_usage_reporting(output_directory: Path, template_directory: Path):
)
frc_net_comm = output_directory / f"main/java/{java_package}/FRCNetComm.java"
frc_net_comm.write_text(contents, encoding="utf-8")
frc_net_comm.write_text(contents, encoding="utf-8", newline="\n")
with (template_directory / "FRCUsageReporting.h.in").open(
encoding="utf-8"
@@ -65,11 +65,26 @@ def generate_usage_reporting(output_directory: Path, template_directory: Path):
usage_reporting_hdr = (
output_directory / "main/native/include/hal/FRCUsageReporting.h"
)
usage_reporting_hdr.write_text(contents, encoding="utf-8")
usage_reporting_hdr.write_text(contents, encoding="utf-8", newline="\n")
with (template_directory / "UsageReporting.h.in").open(
encoding="utf-8"
) as cpp_usage_reporting:
contents = (
# fmt: off
cpp_usage_reporting.read()
.replace(r"${usage_reporting_types_cpp}", "\n".join(usage_reporting_types_cpp))
.replace(r"${usage_reporting_instances_cpp}", "\n".join(usage_reporting_instances_cpp))
# fmt: on
)
usage_reporting_hdr = (
output_directory / "main/native/include/hal/UsageReporting.h"
)
usage_reporting_hdr.write_text(contents, encoding="utf-8", newline="\n")
def main(argv):
def main():
dirname = Path(__file__).parent
parser = argparse.ArgumentParser()
@@ -85,10 +100,10 @@ def main(argv):
default=dirname / "src/generate",
type=Path,
)
args = parser.parse_args(argv)
args = parser.parse_args()
generate_usage_reporting(args.output_directory, args.template_root)
if __name__ == "__main__":
main(sys.argv[1:])
main()

View File

@@ -4,6 +4,7 @@ kLanguage_Java = 3
kLanguage_Python = 4
kLanguage_DotNet = 5
kLanguage_Kotlin = 6
kLanguage_Rust = 7
kCANPlugin_BlackJagBridge = 1
kCANPlugin_2CAN = 2
kFramework_Iterative = 1
@@ -13,6 +14,7 @@ kFramework_Timed = 4
kFramework_ROS = 5
kFramework_RobotBuilder = 6
kFramework_AdvantageKit = 7
kFramework_MagicBot = 8
kRobotDrive_ArcadeStandard = 1
kRobotDrive_ArcadeButtonSpin = 2
kRobotDrive_ArcadeRatioCurve = 3
@@ -26,6 +28,11 @@ kRobotDrive2_MecanumCartesian = 10
kRobotDrive2_MecanumPolar = 11
kRobotDrive2_KilloughCartesian = 12
kRobotDrive2_KilloughPolar = 13
kRobotDriveSwerve_Other = 14
kRobotDriveSwerve_YAGSL = 15
kRobotDriveSwerve_CTRE = 16
kRobotDriveSwerve_MaxSwerve = 17
kRobotDriveSwerve_AdvantageKit = 18
kDriverStationCIO_Analog = 1
kDriverStationCIO_DigitalIn = 2
kDriverStationCIO_DigitalOut = 3
@@ -52,3 +59,12 @@ kKinematics_SwerveDrive = 3
kOdometry_DifferentialDrive = 1
kOdometry_MecanumDrive = 2
kOdometry_SwerveDrive = 3
kDashboard_Unknown = 1
kDashboard_Glass = 2
kDashboard_SmartDashboard = 3
kDashboard_Shuffleboard = 4
kDashboard_Elastic = 5
kDashboard_LabVIEW = 6
kDashboard_AdvantageScope = 7
kDashboard_QFRCDashboard = 8
kDashboard_FRCWebComponents = 9

View File

@@ -114,3 +114,4 @@ kResourceType_Redux_future4 = 112
kResourceType_Redux_future5 = 113
kResourceType_RevSparkFlexCAN = 114
kResourceType_RevSparkFlexPWM = 115
kResourceType_BangBangController = 116

View File

@@ -0,0 +1,55 @@
#ifndef __UsageReporting_h__
#define __UsageReporting_h__
#ifdef _WIN32
#include <stdint.h>
#define EXPORT_FUNC __declspec(dllexport) __cdecl
#elif defined(__vxworks)
#include <vxWorks.h>
#define EXPORT_FUNC
#else
#include <stdint.h>
#include <stdlib.h>
#define EXPORT_FUNC
#endif
#define kUsageReporting_version 1
namespace nUsageReporting
{
typedef enum
{
${usage_reporting_types_cpp}
// kResourceType_MaximumID = 255,
} tResourceType;
typedef enum
{
${usage_reporting_instances_cpp}
} tInstances;
/**
* Report the usage of a resource of interest.
*
* @param resource one of the values in the tResourceType above (max value 51).
* @param instanceNumber an index that identifies the resource instance.
* @param context an optional additional context number for some cases (such as module number). Set to 0 to omit.
* @param feature a string to be included describing features in use on a specific resource. Setting the same resource more than once allows you to change the feature string.
*/
uint32_t EXPORT_FUNC report(tResourceType resource, uint8_t instanceNumber, uint8_t context = 0, const char* feature = NULL);
} // namespace nUsageReporting
#ifdef __cplusplus
extern "C"
{
#endif
uint32_t EXPORT_FUNC FRC_NetworkCommunication_nUsageReporting_report(uint8_t resource, uint8_t instanceNumber, uint8_t context, const char* feature);
#ifdef __cplusplus
}
#endif
#endif // __UsageReporting_h__

View File

@@ -251,6 +251,8 @@ public final class FRCNetComm {
public static final int kResourceType_RevSparkFlexCAN = 114;
/** kResourceType_RevSparkFlexPWM = 115. */
public static final int kResourceType_RevSparkFlexPWM = 115;
/** kResourceType_BangBangController = 116. */
public static final int kResourceType_BangBangController = 116;
}
/**
@@ -273,6 +275,8 @@ public final class FRCNetComm {
public static final int kLanguage_DotNet = 5;
/** kLanguage_Kotlin = 6. */
public static final int kLanguage_Kotlin = 6;
/** kLanguage_Rust = 7. */
public static final int kLanguage_Rust = 7;
/** kCANPlugin_BlackJagBridge = 1. */
public static final int kCANPlugin_BlackJagBridge = 1;
/** kCANPlugin_2CAN = 2. */
@@ -291,6 +295,8 @@ public final class FRCNetComm {
public static final int kFramework_RobotBuilder = 6;
/** kFramework_AdvantageKit = 7. */
public static final int kFramework_AdvantageKit = 7;
/** kFramework_MagicBot = 8. */
public static final int kFramework_MagicBot = 8;
/** kRobotDrive_ArcadeStandard = 1. */
public static final int kRobotDrive_ArcadeStandard = 1;
/** kRobotDrive_ArcadeButtonSpin = 2. */
@@ -317,6 +323,16 @@ public final class FRCNetComm {
public static final int kRobotDrive2_KilloughCartesian = 12;
/** kRobotDrive2_KilloughPolar = 13. */
public static final int kRobotDrive2_KilloughPolar = 13;
/** kRobotDriveSwerve_Other = 14. */
public static final int kRobotDriveSwerve_Other = 14;
/** kRobotDriveSwerve_YAGSL = 15. */
public static final int kRobotDriveSwerve_YAGSL = 15;
/** kRobotDriveSwerve_CTRE = 16. */
public static final int kRobotDriveSwerve_CTRE = 16;
/** kRobotDriveSwerve_MaxSwerve = 17. */
public static final int kRobotDriveSwerve_MaxSwerve = 17;
/** kRobotDriveSwerve_AdvantageKit = 18. */
public static final int kRobotDriveSwerve_AdvantageKit = 18;
/** kDriverStationCIO_Analog = 1. */
public static final int kDriverStationCIO_Analog = 1;
/** kDriverStationCIO_DigitalIn = 2. */
@@ -369,6 +385,24 @@ public final class FRCNetComm {
public static final int kOdometry_MecanumDrive = 2;
/** kOdometry_SwerveDrive = 3. */
public static final int kOdometry_SwerveDrive = 3;
/** kDashboard_Unknown = 1. */
public static final int kDashboard_Unknown = 1;
/** kDashboard_Glass = 2. */
public static final int kDashboard_Glass = 2;
/** kDashboard_SmartDashboard = 3. */
public static final int kDashboard_SmartDashboard = 3;
/** kDashboard_Shuffleboard = 4. */
public static final int kDashboard_Shuffleboard = 4;
/** kDashboard_Elastic = 5. */
public static final int kDashboard_Elastic = 5;
/** kDashboard_LabVIEW = 6. */
public static final int kDashboard_LabVIEW = 6;
/** kDashboard_AdvantageScope = 7. */
public static final int kDashboard_AdvantageScope = 7;
/** kDashboard_QFRCDashboard = 8. */
public static final int kDashboard_QFRCDashboard = 8;
/** kDashboard_FRCWebComponents = 9. */
public static final int kDashboard_FRCWebComponents = 9;
}
/** Utility class. */

View File

@@ -167,6 +167,7 @@ namespace HALUsageReporting {
kResourceType_Redux_future5 = 113,
kResourceType_RevSparkFlexCAN = 114,
kResourceType_RevSparkFlexPWM = 115,
kResourceType_BangBangController = 116,
};
enum tInstances : int32_t {
kLanguage_LabVIEW = 1,
@@ -175,6 +176,7 @@ namespace HALUsageReporting {
kLanguage_Python = 4,
kLanguage_DotNet = 5,
kLanguage_Kotlin = 6,
kLanguage_Rust = 7,
kCANPlugin_BlackJagBridge = 1,
kCANPlugin_2CAN = 2,
kFramework_Iterative = 1,
@@ -184,6 +186,7 @@ namespace HALUsageReporting {
kFramework_ROS = 5,
kFramework_RobotBuilder = 6,
kFramework_AdvantageKit = 7,
kFramework_MagicBot = 8,
kRobotDrive_ArcadeStandard = 1,
kRobotDrive_ArcadeButtonSpin = 2,
kRobotDrive_ArcadeRatioCurve = 3,
@@ -197,6 +200,11 @@ namespace HALUsageReporting {
kRobotDrive2_MecanumPolar = 11,
kRobotDrive2_KilloughCartesian = 12,
kRobotDrive2_KilloughPolar = 13,
kRobotDriveSwerve_Other = 14,
kRobotDriveSwerve_YAGSL = 15,
kRobotDriveSwerve_CTRE = 16,
kRobotDriveSwerve_MaxSwerve = 17,
kRobotDriveSwerve_AdvantageKit = 18,
kDriverStationCIO_Analog = 1,
kDriverStationCIO_DigitalIn = 2,
kDriverStationCIO_DigitalOut = 3,
@@ -223,6 +231,15 @@ namespace HALUsageReporting {
kOdometry_DifferentialDrive = 1,
kOdometry_MecanumDrive = 2,
kOdometry_SwerveDrive = 3,
kDashboard_Unknown = 1,
kDashboard_Glass = 2,
kDashboard_SmartDashboard = 3,
kDashboard_Shuffleboard = 4,
kDashboard_Elastic = 5,
kDashboard_LabVIEW = 6,
kDashboard_AdvantageScope = 7,
kDashboard_QFRCDashboard = 8,
kDashboard_FRCWebComponents = 9,
};
}
#endif

View File

@@ -0,0 +1,240 @@
#ifndef __UsageReporting_h__
#define __UsageReporting_h__
#ifdef _WIN32
#include <stdint.h>
#define EXPORT_FUNC __declspec(dllexport) __cdecl
#elif defined(__vxworks)
#include <vxWorks.h>
#define EXPORT_FUNC
#else
#include <stdint.h>
#include <stdlib.h>
#define EXPORT_FUNC
#endif
#define kUsageReporting_version 1
namespace nUsageReporting
{
typedef enum
{
kResourceType_Controller = 0,
kResourceType_Module = 1,
kResourceType_Language = 2,
kResourceType_CANPlugin = 3,
kResourceType_Accelerometer = 4,
kResourceType_ADXL345 = 5,
kResourceType_AnalogChannel = 6,
kResourceType_AnalogTrigger = 7,
kResourceType_AnalogTriggerOutput = 8,
kResourceType_CANJaguar = 9,
kResourceType_Compressor = 10,
kResourceType_Counter = 11,
kResourceType_Dashboard = 12,
kResourceType_DigitalInput = 13,
kResourceType_DigitalOutput = 14,
kResourceType_DriverStationCIO = 15,
kResourceType_DriverStationEIO = 16,
kResourceType_DriverStationLCD = 17,
kResourceType_Encoder = 18,
kResourceType_GearTooth = 19,
kResourceType_Gyro = 20,
kResourceType_I2C = 21,
kResourceType_Framework = 22,
kResourceType_Jaguar = 23,
kResourceType_Joystick = 24,
kResourceType_Kinect = 25,
kResourceType_KinectStick = 26,
kResourceType_PIDController = 27,
kResourceType_Preferences = 28,
kResourceType_PWM = 29,
kResourceType_Relay = 30,
kResourceType_RobotDrive = 31,
kResourceType_SerialPort = 32,
kResourceType_Servo = 33,
kResourceType_Solenoid = 34,
kResourceType_SPI = 35,
kResourceType_Task = 36,
kResourceType_Ultrasonic = 37,
kResourceType_Victor = 38,
kResourceType_Button = 39,
kResourceType_Command = 40,
kResourceType_AxisCamera = 41,
kResourceType_PCVideoServer = 42,
kResourceType_SmartDashboard = 43,
kResourceType_Talon = 44,
kResourceType_HiTechnicColorSensor = 45,
kResourceType_HiTechnicAccel = 46,
kResourceType_HiTechnicCompass = 47,
kResourceType_SRF08 = 48,
kResourceType_AnalogOutput = 49,
kResourceType_VictorSP = 50,
kResourceType_PWMTalonSRX = 51,
kResourceType_CANTalonSRX = 52,
kResourceType_ADXL362 = 53,
kResourceType_ADXRS450 = 54,
kResourceType_RevSPARK = 55,
kResourceType_MindsensorsSD540 = 56,
kResourceType_DigitalGlitchFilter = 57,
kResourceType_ADIS16448 = 58,
kResourceType_PDP = 59,
kResourceType_PCM = 60,
kResourceType_PigeonIMU = 61,
kResourceType_NidecBrushless = 62,
kResourceType_CANifier = 63,
kResourceType_TalonFX = 64,
kResourceType_CTRE_future1 = 65,
kResourceType_CTRE_future2 = 66,
kResourceType_CTRE_future3 = 67,
kResourceType_CTRE_future4 = 68,
kResourceType_CTRE_future5 = 69,
kResourceType_CTRE_future6 = 70,
kResourceType_LinearFilter = 71,
kResourceType_XboxController = 72,
kResourceType_UsbCamera = 73,
kResourceType_NavX = 74,
kResourceType_Pixy = 75,
kResourceType_Pixy2 = 76,
kResourceType_ScanseSweep = 77,
kResourceType_Shuffleboard = 78,
kResourceType_CAN = 79,
kResourceType_DigilentDMC60 = 80,
kResourceType_PWMVictorSPX = 81,
kResourceType_RevSparkMaxPWM = 82,
kResourceType_RevSparkMaxCAN = 83,
kResourceType_ADIS16470 = 84,
kResourceType_PIDController2 = 85,
kResourceType_ProfiledPIDController = 86,
kResourceType_Kinematics = 87,
kResourceType_Odometry = 88,
kResourceType_Units = 89,
kResourceType_TrapezoidProfile = 90,
kResourceType_DutyCycle = 91,
kResourceType_AddressableLEDs = 92,
kResourceType_FusionVenom = 93,
kResourceType_CTRE_future7 = 94,
kResourceType_CTRE_future8 = 95,
kResourceType_CTRE_future9 = 96,
kResourceType_CTRE_future10 = 97,
kResourceType_CTRE_future11 = 98,
kResourceType_CTRE_future12 = 99,
kResourceType_CTRE_future13 = 100,
kResourceType_CTRE_future14 = 101,
kResourceType_ExponentialProfile = 102,
kResourceType_PS4Controller = 103,
kResourceType_PhotonCamera = 104,
kResourceType_PhotonPoseEstimator = 105,
kResourceType_PathPlannerPath = 106,
kResourceType_PathPlannerAuto = 107,
kResourceType_PathFindingCommand = 108,
kResourceType_Redux_future1 = 109,
kResourceType_Redux_future2 = 110,
kResourceType_Redux_future3 = 111,
kResourceType_Redux_future4 = 112,
kResourceType_Redux_future5 = 113,
kResourceType_RevSparkFlexCAN = 114,
kResourceType_RevSparkFlexPWM = 115,
kResourceType_BangBangController = 116,
// kResourceType_MaximumID = 255,
} tResourceType;
typedef enum
{
kLanguage_LabVIEW = 1,
kLanguage_CPlusPlus = 2,
kLanguage_Java = 3,
kLanguage_Python = 4,
kLanguage_DotNet = 5,
kLanguage_Kotlin = 6,
kLanguage_Rust = 7,
kCANPlugin_BlackJagBridge = 1,
kCANPlugin_2CAN = 2,
kFramework_Iterative = 1,
kFramework_Simple = 2,
kFramework_CommandControl = 3,
kFramework_Timed = 4,
kFramework_ROS = 5,
kFramework_RobotBuilder = 6,
kFramework_AdvantageKit = 7,
kFramework_MagicBot = 8,
kRobotDrive_ArcadeStandard = 1,
kRobotDrive_ArcadeButtonSpin = 2,
kRobotDrive_ArcadeRatioCurve = 3,
kRobotDrive_Tank = 4,
kRobotDrive_MecanumPolar = 5,
kRobotDrive_MecanumCartesian = 6,
kRobotDrive2_DifferentialArcade = 7,
kRobotDrive2_DifferentialTank = 8,
kRobotDrive2_DifferentialCurvature = 9,
kRobotDrive2_MecanumCartesian = 10,
kRobotDrive2_MecanumPolar = 11,
kRobotDrive2_KilloughCartesian = 12,
kRobotDrive2_KilloughPolar = 13,
kRobotDriveSwerve_Other = 14,
kRobotDriveSwerve_YAGSL = 15,
kRobotDriveSwerve_CTRE = 16,
kRobotDriveSwerve_MaxSwerve = 17,
kRobotDriveSwerve_AdvantageKit = 18,
kDriverStationCIO_Analog = 1,
kDriverStationCIO_DigitalIn = 2,
kDriverStationCIO_DigitalOut = 3,
kDriverStationEIO_Acceleration = 1,
kDriverStationEIO_AnalogIn = 2,
kDriverStationEIO_AnalogOut = 3,
kDriverStationEIO_Button = 4,
kDriverStationEIO_LED = 5,
kDriverStationEIO_DigitalIn = 6,
kDriverStationEIO_DigitalOut = 7,
kDriverStationEIO_FixedDigitalOut = 8,
kDriverStationEIO_PWM = 9,
kDriverStationEIO_Encoder = 10,
kDriverStationEIO_TouchSlider = 11,
kADXL345_SPI = 1,
kADXL345_I2C = 2,
kCommand_Scheduler = 1,
kCommand2_Scheduler = 2,
kSmartDashboard_Instance = 1,
kSmartDashboard_LiveWindow = 2,
kKinematics_DifferentialDrive = 1,
kKinematics_MecanumDrive = 2,
kKinematics_SwerveDrive = 3,
kOdometry_DifferentialDrive = 1,
kOdometry_MecanumDrive = 2,
kOdometry_SwerveDrive = 3,
kDashboard_Unknown = 1,
kDashboard_Glass = 2,
kDashboard_SmartDashboard = 3,
kDashboard_Shuffleboard = 4,
kDashboard_Elastic = 5,
kDashboard_LabVIEW = 6,
kDashboard_AdvantageScope = 7,
kDashboard_QFRCDashboard = 8,
kDashboard_FRCWebComponents = 9,
} tInstances;
/**
* Report the usage of a resource of interest.
*
* @param resource one of the values in the tResourceType above (max value 51).
* @param instanceNumber an index that identifies the resource instance.
* @param context an optional additional context number for some cases (such as module number). Set to 0 to omit.
* @param feature a string to be included describing features in use on a specific resource. Setting the same resource more than once allows you to change the feature string.
*/
uint32_t EXPORT_FUNC report(tResourceType resource, uint8_t instanceNumber, uint8_t context = 0, const char* feature = NULL);
} // namespace nUsageReporting
#ifdef __cplusplus
extern "C"
{
#endif
uint32_t EXPORT_FUNC FRC_NetworkCommunication_nUsageReporting_report(uint8_t resource, uint8_t instanceNumber, uint8_t context, const char* feature);
#ifdef __cplusplus
}
#endif
#endif // __UsageReporting_h__

View File

@@ -19,16 +19,4 @@ HAL_ENUM(HAL_I2CPort) {
HAL_I2C_kOnboard,
HAL_I2C_kMXP
};
#ifdef __cplusplus
namespace hal {
/**
* A move-only C++ wrapper around HAL_I2CPort.
* Does not ensure destruction.
*/
using I2CPort = Handle<HAL_I2CPort, nullptr, HAL_I2C_kInvalid>;
} // namespace hal
#endif
/** @} */

View File

@@ -41,16 +41,4 @@ HAL_ENUM(HAL_SPIMode) {
/** Clock idle high, data sampled on rising edge. */
HAL_SPI_kMode3 = 3,
};
#ifdef __cplusplus
namespace hal {
/**
* A move-only C++ wrapper around HAL_SPIPort.
* Does not ensure destruction.
*/
using SPIPort = Handle<HAL_SPIPort, nullptr, HAL_SPI_kInvalid>;
} // namespace hal
#endif
/** @} */

View File

@@ -55,6 +55,7 @@ struct FRCDriverStation {
~FRCDriverStation() { gShutdown = true; }
wpi::EventVector newDataEvents;
wpi::mutex cacheMutex;
wpi::mutex tcpCacheMutex;
};
} // namespace
@@ -90,6 +91,29 @@ static std::atomic<JoystickDataCache*> currentCache{nullptr};
static JoystickDataCache* lastGiven = &caches[1];
static JoystickDataCache* cacheToUpdate = &caches[2];
namespace {
struct TcpCache {
TcpCache() { std::memset(this, 0, sizeof(*this)); }
void Update();
void CloneTo(TcpCache* other) { std::memcpy(other, this, sizeof(*this)); }
HAL_MatchInfo matchInfo;
HAL_JoystickDescriptor descriptors[HAL_kMaxJoysticks];
};
static_assert(std::is_standard_layout_v<TcpCache>);
} // namespace
static TcpCache tcpCache;
static TcpCache tcpCurrent;
void TcpCache::Update() {
SimDriverStationData->GetMatchInfo(&matchInfo);
for (int i = 0; i < HAL_kMaxJoysticks; i++) {
SimDriverStationData->GetJoystickDescriptor(i, &descriptors[i]);
}
}
static ::FRCDriverStation* driverStation;
namespace hal::init {
@@ -256,32 +280,47 @@ void HAL_GetAllJoystickData(HAL_JoystickAxes* axes, HAL_JoystickPOVs* povs,
int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
HAL_JoystickDescriptor* desc) {
CHECK_JOYSTICK_NUMBER(joystickNum);
SimDriverStationData->GetJoystickDescriptor(joystickNum, desc);
std::scoped_lock lock{driverStation->tcpCacheMutex};
*desc = tcpCurrent.descriptors[joystickNum];
return 0;
}
HAL_Bool HAL_GetJoystickIsXbox(int32_t joystickNum) {
HAL_JoystickDescriptor desc;
SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
return desc.isXbox;
HAL_JoystickDescriptor joystickDesc;
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
return 0;
} else {
return joystickDesc.isXbox;
}
}
int32_t HAL_GetJoystickType(int32_t joystickNum) {
HAL_JoystickDescriptor desc;
SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
return desc.type;
HAL_JoystickDescriptor joystickDesc;
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
return -1;
} else {
return joystickDesc.type;
}
}
void HAL_GetJoystickName(struct WPI_String* name, int32_t joystickNum) {
HAL_JoystickDescriptor desc;
SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
size_t len = std::strlen(desc.name);
HAL_JoystickDescriptor joystickDesc;
const char* cName = joystickDesc.name;
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
cName = "";
}
auto len = std::strlen(cName);
auto write = WPI_AllocateString(name, len);
std::memcpy(write, desc.name, len);
std::memcpy(write, cName, len);
}
int32_t HAL_GetJoystickAxisType(int32_t joystickNum, int32_t axis) {
return 0;
HAL_JoystickDescriptor joystickDesc;
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
return -1;
} else {
return joystickDesc.axisTypes[axis];
}
}
int32_t HAL_SetJoystickOutputs(int32_t joystickNum, int64_t outputs,
@@ -300,7 +339,8 @@ double HAL_GetMatchTime(int32_t* status) {
}
int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
SimDriverStationData->GetMatchInfo(info);
std::scoped_lock lock{driverStation->tcpCacheMutex};
*info = tcpCurrent.matchInfo;
return 0;
}
@@ -329,31 +369,42 @@ HAL_Bool HAL_RefreshDSData(void) {
return false;
}
bool dsAttached = SimDriverStationData->dsAttached;
std::scoped_lock lock{driverStation->cacheMutex};
JoystickDataCache* prev = currentCache.exchange(nullptr);
if (prev != nullptr) {
currentRead = prev;
JoystickDataCache* prev;
{
std::scoped_lock lock{driverStation->cacheMutex};
prev = currentCache.exchange(nullptr);
if (prev != nullptr) {
currentRead = prev;
}
// If newest state shows we have a DS attached, just use the
// control word out of the cache, As it will be the one in sync
// with the data. If no data has been updated, at this point,
// and a DS wasn't attached previously, this will still return
// a zeroed out control word, with is the correct state for
// no new data.
if (!dsAttached) {
// If the DS is not attached, we need to zero out the control word.
// This is because HAL_RefreshDSData is called asynchronously from
// the DS data. The dsAttached variable comes directly from netcomm
// and could be updated before the caches are. If that happens,
// we would end up returning the previous cached control word,
// which is out of sync with the current control word and could
// break invariants such as which alliance station is in used.
// Also, when the DS has never been connected the rest of the fields
// in control word are garbage, so we also need to zero out in that
// case too
std::memset(&currentRead->controlWord, 0,
sizeof(currentRead->controlWord));
}
newestControlWord = currentRead->controlWord;
}
// If newest state shows we have a DS attached, just use the
// control word out of the cache, As it will be the one in sync
// with the data. If no data has been updated, at this point,
// and a DS wasn't attached previously, this will still return
// a zeroed out control word, with is the correct state for
// no new data.
if (!dsAttached) {
// If the DS is not attached, we need to zero out the control word.
// This is because HAL_RefreshDSData is called asynchronously from
// the DS data. The dsAttached variable comes directly from netcomm
// and could be updated before the caches are. If that happens,
// we would end up returning the previous cached control word,
// which is out of sync with the current control word and could
// break invariants such as which alliance station is in used.
// Also, when the DS has never been connected the rest of the fields
// in control word are garbage, so we also need to zero out in that
// case too
std::memset(&currentRead->controlWord, 0, sizeof(currentRead->controlWord));
{
tcpCache.Update();
std::scoped_lock tcpLock(driverStation->tcpCacheMutex);
tcpCache.CloneTo(&tcpCurrent);
}
newestControlWord = currentRead->controlWord;
return prev != nullptr;
}

View File

@@ -317,7 +317,7 @@ HAL_SimDeviceHandle SimDeviceData::GetDeviceHandle(const char* name) {
if (it == m_deviceMap.end()) {
return 0;
}
if (auto deviceImpl = it->getValue().lock()) {
if (auto deviceImpl = it->second.lock()) {
return deviceImpl->handle;
} else {
return 0;
@@ -459,10 +459,10 @@ HAL_SimValueHandle SimDeviceData::GetValueHandle(HAL_SimDeviceHandle device,
if (it == deviceImpl->valueMap.end()) {
return 0;
}
if (!it->getValue()) {
if (!it->second) {
return 0;
}
return it->getValue()->handle;
return it->second->handle;
}
void SimDeviceData::EnumerateValues(HAL_SimDeviceHandle device, void* param,

View File

@@ -131,6 +131,8 @@ TEST(DriverStationTest, EventInfo) {
info.replayNumber = 42;
HALSIM_SetMatchInfo(&info);
HAL_RefreshDSData();
HAL_MatchInfo dataBack;
HAL_GetMatchInfo(&dataBack);

92
ntcore/BUILD.bazel Normal file
View File

@@ -0,0 +1,92 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
cc_library(
name = "generated_cc_headers",
hdrs = glob(["src/generated/main/native/include/**"]),
includes = ["src/generated/main/native/include"],
strip_include_prefix = "src/generated/main/native/include",
visibility = ["//ntcore:__subpackages__"],
)
filegroup(
name = "generated_cc_source",
srcs = glob(
["src/generated/main/native/cpp/**"],
exclude = ["src/generated/main/native/cpp/jni/**"],
),
visibility = ["//ntcore:__subpackages__"],
)
filegroup(
name = "generated_java",
srcs = glob(["src/generated/main/java/**/*.java"]),
visibility = ["//ntcore:__subpackages__"],
)
cc_library(
name = "ntcore.static",
srcs = glob(
["src/main/native/cpp/**"],
exclude = ["src/main/native/cpp/jni/**"],
) + [":generated_cc_source"],
hdrs = glob(["src/main/native/include/**/*"]),
includes = [
"src/main/native/cpp",
"src/main/native/include",
],
strip_include_prefix = "src/main/native/include",
visibility = ["//visibility:public"],
deps = [
":generated_cc_headers",
"//wpinet:wpinet.static",
"//wpiutil:wpiutil.static",
],
)
java_library(
name = "networktables-java",
srcs = glob(["src/main/java/**/*.java"]) + [":generated_java"],
visibility = ["//visibility:public"],
deps = [
"//wpiutil:wpiutil-java",
"@maven//:us_hebi_quickbuf_quickbuf_runtime",
],
)
cc_test(
name = "ntcore-cpp-test",
size = "small",
srcs = glob([
"src/test/native/**/*.cpp",
"src/test/native/**/*.h",
]),
tags = [
"exclusive",
"no-asan",
"no-tsan",
],
deps = [
":ntcore.static",
"//thirdparty/googletest:googletest.static",
"//wpiutil:wpiutil-testlib",
],
)
cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
deps = [
":ntcore.static",
],
)
java_binary(
name = "DevMain-Java",
srcs = ["src/dev/java/edu/wpi/first/ntcore/DevMain.java"],
main_class = "edu.wpi.first.ntcore.DevMain",
deps = [
"networktables-java",
"//wpiutil:wpiutil-java",
],
)

View File

@@ -7,9 +7,11 @@ file(
GLOB ntcore_native_src
src/main/native/cpp/*.cpp
src/generated/main/native/cpp/*.cpp
src/main/native/cpp/local/*.cpp
src/main/native/cpp/net/*.cpp
src/main/native/cpp/net3/*.cpp
src/main/native/cpp/networktables/*.cpp
src/main/native/cpp/server/*.cpp
src/main/native/cpp/tables/*.cpp
)
add_library(ntcore ${ntcore_native_src})

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