Compare commits

...

178 Commits

Author SHA1 Message Date
Peter Johnson
3355383fe9 Merge branch 'main' into 2027 2025-06-13 22:26:09 -07:00
Thad House
fbee476fd2 [hal] Use new canbus names for systemcore (#8022)
The canbus names are changing to ensure stable ordering.
2025-06-13 21:30:42 -07:00
PJ Reiniger
fbbc4bc53c [bazel] Clean up bazel scripts (#7984) 2025-06-13 20:53:09 -07:00
Michael Lesirge
05c080328b [wpimath] Pass Translation2d by const reference instead of by value (#8021) 2025-06-13 18:50:05 -07:00
Michael Lesirge
c01e318370 [wpimath] Add Translation3d.nearest() (#8015) 2025-06-12 22:14:00 -07:00
Ryan Blue
5dfc664b93 [hal, wpilib] Add systemcore IMU (#8016) 2025-06-10 21:57:42 -07:00
Ryan Blue
89b97a21d8 [ntcore] Change 'null' to 'empty string' in NT StartServer docs (NFC) (#8017) 2025-06-10 21:55:51 -07:00
Michael Lesirge
075cc4a20f [wpimath] Add nearest() method to Pose3d (mirroring Pose2d) (#8010) 2025-06-05 22:11:49 -07:00
Peter Johnson
f99692f287 [ntcore] Minimize latency on localhost connections (#7997) 2025-06-02 16:43:18 -07:00
Thad House
2af8c59858 Replace /home/lvuser with /home/systemcore (#8002) 2025-06-02 16:42:56 -07:00
Thad House
4d74ea6278 [wpilib] Remove version writes (#8003)
This will use a much different mechanism in the future.
2025-06-02 16:41:47 -07:00
Thad House
a4cf2ea6ec [hal] Fix dutyCycle high time units (#8000)
The units are nanoseconds, not microseconds
2025-06-01 22:24:48 -07:00
Thad House
b205f3e1b4 [hal] Add temp value for HAL_GetUserVoltage3V3 (#8001)
Lots of higher level code in allwpilib depends on this, so we should return at least a valid and sane value, rather then erroring.
2025-06-01 22:23:51 -07:00
Thad House
be67432a5e [hal] Remove unnecessary print in CAN initialization (#8004) 2025-06-01 22:21:11 -07:00
DeltaDizzy
1955dcddb3 [wpimath] Fix SimpleMotorFeedforward no-accel overload returning negative voltage outputs (#7999)
SimpleMotorFeedforward::calculate(velocity) was not updated to account for the removal of calculate(velocity, acceleration), so it would pass currentVelocity = velocity and nextVelocity = 0, resulting in negative outputs in many scenarios.
2025-06-01 16:46:54 -07:00
Ryan Blue
e12d78a70a [ci] Deploy 2027 docs on tag (#7998) 2025-06-01 10:00:59 -07:00
Thad House
d3fbebc0a9 [hal] Add systemcore battery reading (#7995) 2025-05-31 10:53:11 -07:00
Thad House
1991af34a5 [hal] Update to new joystick protobuf definitions (#7991) 2025-05-31 10:52:27 -07:00
Ryan Shavell
7a3df6175e [epilogue] Add superclass field & method logging (#7993) 2025-05-31 06:38:51 -07:00
Peter Johnson
a6f601453a [examples] Fix up merge from main (#7994) 2025-05-30 17:36:53 -07:00
Peter Johnson
6c16e846fa Merge branch 'main' into 2027 2025-05-29 21:41:50 -07:00
Gold856
ca05ffa1b9 [upstream_utils] Use pathlib instead of os.path (#7983)
A noteworthy change is the replacement of the `dp.startswith(os.path.join(".", "subdir"))` pattern. pathlib doesn't offer something with similar semantics besides `match` and `full_match`, so there's now a helper function that replicates the behavior.

Other notable changes include the addition of type annotations to ensure code correctness, using == to check file names instead of `endswith` for clarity (`endswith` is still used to check extensions), manual walking and copying being refactored in googletest, json, memory, nanopb, protobuf, and sleipnir to use `walk_cwd_and_copy_if`, and matching functions being shortened to the point where they can just be inlined into the lambda.

Co-authored-by: Tyler Veness <calcmogul@gmail.com>
Co-authored-by: David Vo <auscompgeek@users.noreply.github.com>
2025-05-29 16:05:22 -06:00
Tyler Veness
de718f7ae5 [upstream_utils] Upgrade Sleipnir (#7973) 2025-05-27 08:24:15 -06:00
sciencewhiz
5368e8c6ed [ci] Disable RobotPy build (#7990)
Needs update for meson build
2025-05-27 08:19:39 -06:00
sciencewhiz
25eacfa226 Update frcYear to 2027_alpha1 for vendordeps (#7989) 2025-05-27 08:19:03 -06:00
Thad House
22d12d2345 [wpinet] Add callback for mDNS service resolver (#7986) 2025-05-23 15:22:59 -05:00
Phuc-Thanh Nguyen
abd312f3d0 [wpinet] http_parser: unset F_CHUNKED on new Transfer-Encoding (#7985) 2025-05-22 15:43:20 -06:00
Tyler Veness
b4823569a4 [upstream_utils] Upgrade Eigen to latest (#7982) 2025-05-19 14:26:53 -06:00
Thad House
0cb4df7e05 [hal] Fix joystick buttons not working on SC (#7980) 2025-05-18 07:03:52 -07:00
Thad House
231ec348fe [hal] Update DS API to new format (#7977) 2025-05-16 22:15:14 -07:00
Lucien Morey
d32e60233f [wpimath] Add dynamic size support for angle statistics (#7964) 2025-05-15 18:43:46 -07:00
sciencewhiz
1596e2fd7a [ci] Remove dispatch for tools not supported in 2027 (#7975) 2025-05-15 18:25:34 -07:00
sciencewhiz
79a9d7f987 [examples] Add Accelerometer filtering snippets 2025-05-13 22:03:22 -07:00
sciencewhiz
0877d130be [examples] Add Accelerometer collision detection snippets 2025-05-13 22:03:22 -07:00
sciencewhiz
89555383cc [examples] Add BuiltInAccelerometer Snippet 2025-05-13 22:03:22 -07:00
sciencewhiz
de315947e9 [examples] Add ADXL 345 and 362 snippets 2025-05-13 22:03:22 -07:00
sciencewhiz
b7cd03adc4 [examples] Add AnalogAccelerometer snippets
Delete AnalogAccelerometer comments about sensitivity and zero constants that don't exist
2025-05-13 22:03:22 -07:00
PJ Reiniger
6e3f48daeb [bazel] Add scripts to validate pregeneration tools (#7690) 2025-05-13 22:01:47 -07:00
PJ Reiniger
0e5a6f38d8 [bazel] MVP for building GUI related things with bazel (#7934)
Co-authored-by: David Vo <auscompgeek@users.noreply.github.com>
2025-05-12 08:24:41 -06:00
Tyler Veness
cc8eaf3ed7 [wpilib] Remove unhelpful comments from BooleanEvent (#7970)
These were apparently a meme about state being hard to manage rather
than a statement about the code itself. I spent a while trying to find
some complex logic this comment was alluding to that would indicate why
it's "a nightmare to manage".
2025-05-12 08:19:54 -06:00
Peter Johnson
dd6c830768 Revert "Added parity with C++ units. (#7965)" (#7968)
This reverts commit 3dee19a435.

This was merged without sufficient review or discussion as to whether these units are value-add for the Java units library.
2025-05-11 15:08:31 -07:00
T Grinch
3dee19a435 Added parity with C++ units. (#7965)
* Added parity with C++ units.

* Update Units.java

* Update Units.java

* Formatting

* Fixed formatting

---------

Co-authored-by: thenetworkgrinch <thenetworkgrinch@users.noreply.github.com>
2025-05-10 21:04:17 -04:00
sciencewhiz
55a97f0c11 [examples] Add Analog Potentiometer Snippets (#7957) 2025-05-10 07:18:19 -07:00
Yuhao
b1f7e6d6f2 [cscore] Resolve macOS camera freeze with specific devices (#7960)
Addresses an issue where certain USB cameras, specifically the ArduCam OV9281, would freeze when attempting to stream on macOS.

The previous logic started the AVCaptureSession (startRunning) before locking the device for configuration (lockForConfiguration). While this works for many cameras, it causes the OV9281 to become unresponsive.

Further investigation revealed:
- Moving startRunning to after unlockForConfiguration resulted in macOS overriding the custom format and frame rate settings applied within the lock.
- The reliable solution, inspired by findings shared in the community (e.g., Stack Overflow), is to lock the device, apply the configuration, start the session, and then unlock the device.

This commit reorders the operations within deviceStreamOn in UsbCameraImplObjc.mm to follow the sequence: lockForConfiguration -> apply settings -> startRunning -> unlockForConfiguration. This ensures the desired camera configuration is applied correctly without causing device freezes on problematic hardware like the OV9281.
2025-05-07 19:57:03 -07:00
Gold856
ca3137b291 [build] Remove statically linked JNI artifacts (#7553) 2025-05-05 22:10:07 -07:00
Lucien Morey
e1da917270 [wpimath] Add dynamic size support for numerical jacobian computation (#7959) 2025-05-05 21:55:51 -07:00
Peter Johnson
0a38400734 [datalog] Finish up a few missed moves from wpiutil split (#7956) 2025-05-04 11:21:29 -07:00
sciencewhiz
b96264f042 [examples] Misc fixes for snippets (#7952) 2025-05-04 00:13:46 -07:00
sciencewhiz
a15152712f [examples] Add AnalogInput snippets (#7951) 2025-05-04 00:13:20 -07:00
Eric
17cae787e7 [commands] Mark CommandPtr class as [[nodiscard]] (#7803)
This has the same effect but makes it so any user code returning CommandPtr can't discard a returned command.

Signed-off-by: Eric Ward <ezeward4@gmail.com>
2025-05-03 20:44:55 -07:00
Jade
02de5f710e [cscore] Fix memory leak in usbviewer example (#7922)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-05-01 22:07:45 -07:00
sciencewhiz
e63899e63a [examples] Add snippets for Digital Input article (#7949) 2025-05-01 11:40:38 -06:00
Tyler Veness
ba37e7eb3c [wpimath] Fix warning false positive from GCC 15 (#7948)
```
In file included from /usr/include/c++/15.1.1/functional:61,
                 from /home/tav/frc/wpilib/allwpilib/thirdparty/googletest/include/gtest/gtest-matchers.h:43,
                 from /home/tav/frc/wpilib/allwpilib/thirdparty/googletest/include/gtest/internal/gtest-death-test-internal.h:47,
                 from /home/tav/frc/wpilib/allwpilib/thirdparty/googletest/include/gtest/gtest-death-test.h:43,
                 from /home/tav/frc/wpilib/allwpilib/thirdparty/googletest/include/gtest/gtest.h:65,
                 from /home/tav/frc/wpilib/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp:7:
In copy constructor ‘std::function<_Res(_ArgTypes ...)>::function(const std::function<_Res(_ArgTypes ...)>&) [with _Res = Eigen::Matrix<double, 2, 1>; _ArgTypes = {const Eigen::Matrix<double, 2, 1, 0, 2, 1>&, const Eigen::Matrix<double, 1, 1, 0, 1, 1>&}]’,
    inlined from ‘frc::ControlAffinePlantInversionFeedforward<States, Inputs>::ControlAffinePlantInversionFeedforward(std::function<Eigen::Matrix<double, Size, 1, (Eigen::AutoAlign | (((Size == 1) && (1 != 1)) ? Eigen::RowMajor : (((1 == 1) && (Size != 1)) ? Eigen::ColMajor :  Eigen::ColMajor))), Size, 1>(const Eigen::Matrix<double, Size, 1, (Eigen::AutoAlign | (((Size == 1) && (1 != 1)) ? Eigen::RowMajor : (((1 == 1) && (Size != 1)) ? Eigen::ColMajor :  Eigen::ColMajor))), Size, 1>&, const Eigen::Matrix<double, Cols, 1, (Eigen::AutoAlign | (((Cols == 1) && (1 != 1)) ? Eigen::RowMajor : (((1 == 1) && (Cols != 1)) ? Eigen::ColMajor :  Eigen::ColMajor))), Cols, 1>&)>, units::time::second_t) [with int States = 2; int Inputs = 1]’ at /home/tav/frc/wpilib/allwpilib/wpimath/src/main/native/include/frc/controller/ControlAffinePlantInversionFeedforward.h:59:19,
    inlined from ‘virtual void frc::ControlAffinePlantInversionFeedforwardTest_Calculate_Test::TestBody()’ at /home/tav/frc/wpilib/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp:29:70:
/usr/include/c++/15.1.1/bits/std_function.h:393:17: error: ‘<anonymous>’ may be used uninitialized [-Werror=maybe-uninitialized]
  393 |             __x._M_manager(_M_functor, __x._M_functor, __clone_functor);
      |             ~~~~^~~~~~~~~~
/usr/include/c++/15.1.1/bits/std_function.h: In member function ‘virtual void frc::ControlAffinePlantInversionFeedforwardTest_Calculate_Test::TestBody()’:
/usr/include/c++/15.1.1/bits/std_function.h:269: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 = Eigen::Matrix<double, 2, 1>; _Functor = frc::ControlAffinePlantInversionFeedforwardTest_Calculate_Test::TestBody()::<lambda(auto:45&, auto:46&)>; _ArgTypes = {const Eigen::Matrix<double, 2, 1, 0, 2, 1>&, const Eigen::Matrix<double, 1, 1, 0, 1, 1>&}]’ declared here
  269 |       _M_manager(_Any_data& __dest, const _Any_data& __source,
      |       ^~~~~~~~~~
/home/tav/frc/wpilib/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp:29:70: note: ‘<anonymous>’ declared here
   29 |                                                                 20_ms};
      |                                                                      ^
In copy constructor ‘std::function<_Res(_ArgTypes ...)>::function(const std::function<_Res(_ArgTypes ...)>&) [with _Res = Eigen::Matrix<double, 2, 1>; _ArgTypes = {const Eigen::Matrix<double, 2, 1, 0, 2, 1>&}]’,
    inlined from ‘frc::ControlAffinePlantInversionFeedforward<States, Inputs>::ControlAffinePlantInversionFeedforward(std::function<Eigen::Matrix<double, Size, 1, (Eigen::AutoAlign | (((Size == 1) && (1 != 1)) ? Eigen::RowMajor : (((1 == 1) && (Size != 1)) ? Eigen::ColMajor :  Eigen::ColMajor))), Size, 1>(const Eigen::Matrix<double, Size, 1, (Eigen::AutoAlign | (((Size == 1) && (1 != 1)) ? Eigen::RowMajor : (((1 == 1) && (Size != 1)) ? Eigen::ColMajor :  Eigen::ColMajor))), Size, 1>&)>, frc::Matrixd<Rows, Cols>&, units::time::second_t) [with int States = 2; int Inputs = 1]’ at /home/tav/frc/wpilib/allwpilib/wpimath/src/main/native/include/frc/controller/ControlAffinePlantInversionFeedforward.h:79:11,
    inlined from ‘virtual void frc::ControlAffinePlantInversionFeedforwardTest_CalculateState_Test::TestBody()’ at /home/tav/frc/wpilib/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp:45:73:
/usr/include/c++/15.1.1/bits/std_function.h:393:17: error: ‘<anonymous>’ may be used uninitialized [-Werror=maybe-uninitialized]
  393 |             __x._M_manager(_M_functor, __x._M_functor, __clone_functor);
      |             ~~~~^~~~~~~~~~
/usr/include/c++/15.1.1/bits/std_function.h: In member function ‘virtual void frc::ControlAffinePlantInversionFeedforwardTest_CalculateState_Test::TestBody()’:
/usr/include/c++/15.1.1/bits/std_function.h:269: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 = Eigen::Matrix<double, 2, 1>; _Functor = frc::ControlAffinePlantInversionFeedforwardTest_CalculateState_Test::TestBody()::<lambda(auto:47&)>; _ArgTypes = {const Eigen::Matrix<double, 2, 1, 0, 2, 1>&}]’ declared here
  269 |       _M_manager(_Any_data& __dest, const _Any_data& __source,
      |       ^~~~~~~~~~
/home/tav/frc/wpilib/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp:45:73: note: ‘<anonymous>’ declared here
   45 |                                                                 B, 20_ms};
      |                                                                         ^
```
2025-05-01 11:40:13 -06:00
Cory
7036bd509e [wpimath] Correct mil unit (#7950) 2025-05-01 11:28:02 -06:00
Yuhao
8fe3cfb325 [cscore] Add UVC Protocol Support for USB Camera Controls on macOS (#7926) 2025-04-30 08:02:59 -06:00
Jonah Snider
a4572a01b7 [wpiutil] Add nested struct schemas before parent schema (#7935)
When adding struct schemas, the current logic is to add the parent/outer schema, and then add the schemas for any nested inner schemas.  This reverses that order to make it easier for tools to process.

Matches C++ logic.
2025-04-29 20:52:27 -07:00
Lucien Morey
f14af97dc7 [wpimath] Support dynamic matrix sizes in StateSpaceUtil (#7942) 2025-04-29 11:58:28 -06:00
Jade
631521a980 [thirdparty] Set IMGUI_DEFINE_MATH_OPERATORS (#7899)
This avoids the need to define it early in the include order everywhere else.

Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-04-29 08:44:17 -06:00
sciencewhiz
c49fc29046 [examples] Add Limit Switch snippets (#7946) 2025-04-29 08:42:32 -06:00
Nolan Barker
40ce42712f [wpimath] Fix coordinate frame docs in HolonomicDriveController (#7938) 2025-04-28 16:27:38 -07:00
sciencewhiz
659710a79a [examples] Add remaining Encoder snippets (#7936) 2025-04-28 16:25:49 -07:00
David Vo
d059cbc157 [bazel] Hide wpinet implementation headers (#7941)
The headers in wpinet/src/main/native/cpp/*.h aren't intended to be used by dependents, so they shouldn't get propagated.
2025-04-28 16:46:22 -06:00
Thad House
08297430b5 [hal,wpilib] Add support for second I2C port (#7878) 2025-04-28 09:29:01 -06:00
Thad House
85a8fc9943 [hal] Add SystemCore to Java runtime type (#7932) 2025-04-28 09:10:32 -06:00
Peter Johnson
36811211be Merge branch 'main' into 2027 2025-04-25 23:45:43 -07:00
Gold856
2f0990e9d2 [commands] Remove control commands and subsystems (#7921) 2025-04-25 22:06:26 -07:00
crueter
0695a4db89 [wpimath] Fix Debouncer type-changing behavior (#7870)
Closes #7867

Properly resets the baseline upon switching the debounce type, and adds
a test for such.

Signed-off-by: swurl <swurl@swurl.xyz>
2025-04-25 22:05:31 -07:00
me-it-is
3960045663 [wpimath] TrapezoidProfile: Fix null pointer when calling timeLeftUntil (#7894) 2025-04-25 21:58:05 -07:00
Tyler Veness
92010c175f Fix more emscripten compiler errors (#7895)
I ran the CMake configure with:
```bash
emcmake cmake -B build-wasm -S . \
  -DCMAKE_BUILD_TYPE=Release \
  -DBUILD_SHARED_LIBS=OFF \
  -DWITH_SIMULATION_MODULES=OFF \
  -DWITH_PROTOBUF=OFF \
  -DWITH_GUI=OFF \
  -DWITH_CSCORE=OFF
```

* Turned off simulation modules because they require shared libraries
* Turned off GUI because glfw requires libssh
* Turned off cscore because it requires OpenCV

I still get the following compiler errors:

```
/home/tav/frc/wpilib/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux.cpp:43:10: fatal error: 'sys/epoll.h' file not found
   43 | #include <sys/epoll.h>
      |          ^~~~~~~~~~~~~
```
```
/home/tav/frc/wpilib/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/stream.cpp:991:56: error: comparison of integers of different signs: 'unsigned long' and 'long' [-Werror,-Wsign-compare]
  991 |   for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
      |                                                        ^~~~~~~~~~~~~~~~~~~~~~
/home/tav/.cache/emscripten/sysroot/include/sys/socket.h:358:44: note: expanded from macro 'CMSG_NXTHDR'
  358 |         __CMSG_LEN(cmsg) + sizeof(struct cmsghdr) >= __MHDR_END(mhdr) - (unsigned char *)(cmsg) \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
```
/home/tav/frc/wpilib/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/core.cpp:748:56: error: comparison of integers of different signs: 'unsigned long' and 'long' [-Werror,-Wsign-compare]
  748 |   for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg))
      |                                                        ^~~~~~~~~~~~~~~~~~~~~~
/home/tav/.cache/emscripten/sysroot/include/sys/socket.h:358:44: note: expanded from macro 'CMSG_NXTHDR'
  358 |         __CMSG_LEN(cmsg) + sizeof(struct cmsghdr) >= __MHDR_END(mhdr) - (unsigned char *)(cmsg) \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
2025-04-25 21:56:26 -07:00
sciencewhiz
90ee11a9e0 [examples] Add DutyCycleEncoder Snippets (#7930) 2025-04-25 21:47:53 -07:00
David Vo
828199befb [ci] Use Bazel fastbuild compilation mode on Windows (#7925) 2025-04-25 21:46:40 -07:00
David Vo
d04e15b957 [bazel] Avoid globally linking macOS SDK frameworks (#7927) 2025-04-25 10:37:42 -07:00
Joseph Eng
d98ad815b1 [wpimath] Adjust applyDeadband logic (#7816)
Reduces nesting by returning when the value is within the deadband.

Adjusts the algorithm to handle large values of maxMagnitude naturally (instead of needing a separate check).

Reformats the math comments.
2025-04-23 08:41:03 -06:00
Austin Schuh
1c35a3a5ff [bazel] Make bazel work outside // (#7918)
The .bazelrc was doing a relative import, not an absolute one.

Signed-off-by: Austin Schuh <austin.linux@gmail.com>
2025-04-23 08:31:43 -06:00
Gold856
b248663386 [ci] Upgrade sccache action (#7914) 2025-04-22 15:27:55 -06:00
Gold856
9c60da1319 [ci] Use sccache for RobotPy build (#7915)
Also uses Artifactory to store the caches.
2025-04-22 15:27:20 -06:00
sciencewhiz
21d921184a [examples] Add compilable code snippets (#7909)
This enables frc-docs to use RLIs for things that are currently in-line
code blocks, and ensures they compile, which is important with the 2027
breaking changes coming. They are kept separate from the examples to
ensure they don't polute the VSCode examples finder.

Adds the Encoder snippets used in the frc-docs Encoder article as the
first instance of this.
2025-04-22 15:26:26 -06:00
Yuhao
26e299115f [cscore] Fix USB video mode handling on macOS (#7904)
* fix: UsbCameraImpl on macOS

* fix: add back logs
2025-04-21 09:27:42 -07:00
Wispy
07192285f6 [wpiunits] Add InchesPerSecondPerSecond (#7905) 2025-04-17 19:00:14 -07:00
Tyler Veness
ed94e3af3e [ci] Upgrade sccache action (#7911) 2025-04-17 18:59:10 -07:00
Tyler Veness
3cd282d9b0 [ci] Upgrade to wpiformat 2025.33 (#7902) 2025-04-12 20:29:09 -07:00
Tyler Veness
49b4b064cf [docs] Update readme to say Xcode is required (#7892)
The command line tools are insufficient.
2025-04-08 08:11:15 -06:00
Tyler Veness
bd78215b43 [glass] Fix compilation error with protobuf 30.1 (#7890)
```
/home/tav/frc/wpilib/allwpilib/glass/src/libnt/native/cpp/NetworkTables.cpp: In function ‘void UpdateProtobufValueSource(glass::NetworkTablesModel&, glass::NetworkTablesModel::ValueSource*, const google::protobuf::Message&, std::string_view, int64_t)’:
/home/tav/frc/wpilib/allwpilib/glass/src/libnt/native/cpp/NetworkTables.cpp:363:27: error: no match for ‘operator+’ (operand types are ‘const char [7]’ and ‘google::protobuf::internal::DescriptorStringView’ {aka ‘std::basic_string_view<char>’})
  363 |   out->typeStr = "proto:" + desc->full_name();
      |                  ~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~
      |                  |                         |
      |                  const char [7]            google::protobuf::internal::DescriptorStringView {aka std::basic_string_view<char>}
```
2025-04-07 07:31:31 -06:00
Tyler Veness
c13b2f45c1 [wpimath] Update link to Ramsete controller derivation (#7887)
The derivation was moved from the Controls Engineering in FRC book to a
separate paper.
2025-03-30 16:25:08 -07:00
Jonah Snider
ea986427aa [wpiutil] Add Kaitai Struct definition for data log format (#7882)
Adds a Kaitai Struct definition for the WPILOG format. This can be used to generate code to read/write data log files without needing to build out a parser/serializer by hand. Or just an additional resource for readers to help understand the WPILOG format.
2025-03-27 16:51:13 -06:00
jpokornyiii
dcd397e007 [xrp] Update XRP for new SparkFun RP2350 Board (#7880)
Enables servos 3 and 4 usage on the new XRP board.
2025-03-26 08:50:09 -06:00
Thad House
e2cc9e0059 [hal, wpilib] PWM Rewrite (#7845)
The HAL will only contain the output period and the raw microseconds. Higher level things such as SimDevice can handle everything else.
2025-03-20 19:23:22 -07:00
Thad House
2e21a41f87 [hal] Set number of SmartIO and CAN buses (#7871) 2025-03-19 19:49:54 -07:00
Thad House
52b353fe57 [hal, wpilib] Remove power rails that don't exist on systemcore (#7861) 2025-03-14 10:16:08 -07:00
Tyler Veness
d3cc185382 [upstream_utils] Upgrade to fmt 11.1.4 (#7852) 2025-03-04 18:50:56 -08:00
Tyler Veness
7cb29ce70b [datalog] Fix sorting of related header (#7832) 2025-03-02 10:47:48 -08:00
Thad House
baa20fa239 [hal, wpilib] Rewrite CAN APIs (#7798) 2025-02-25 19:07:01 -08:00
Gold856
b39744b562 [wpimath] Remove PathWeaver support (#7813)
Also rename file load type in glass to "Field Image JSON".
2025-02-21 07:43:45 -07:00
Thad House
72bba2491a [wpilib] Remove Nidec Brushless support (#7811) 2025-02-20 20:14:01 -08:00
Peter Johnson
98f933eca5 Merge branch 'main' into 2027 2025-02-20 00:26:23 -08:00
DeltaDizzy
da47f06d70 [datalog] Move all DataLog functionality to new datalog library (#7641)
Currently the major DataLog backend API (reading and writing) is split between wpiutil and glass. In the interest of allowing code that wants to use these APIs to not need to link to glass and declutter wpiutil, all of those APIs are moved to a new library named "datalog".

Signed-off-by: Jade Turner <spacey-sooty@proton.me>
Co-authored-by: Jade Turner <spacey-sooty@proton.me>
Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>
2025-02-19 21:08:17 -08:00
Tyler Veness
ac1705ae2b [wpimath] Remove unit suffixes from variable names (#7529)
* Move units into API docs instead because suffixes make user code verbose and hard to read
* Rename trackWidth to trackwidth
* Make ultrasonic classes use meters instead of a mix of m, cm, mm, ft,
  and inches
2025-02-10 08:23:04 -07:00
Peter Johnson
764ada9b66 [hal] Change usage reporting to string-based (#7763) 2025-02-07 13:37:23 -07:00
Peter Johnson
bfff891b5c [cameraserver] Remove Axis camera functions (#7767) 2025-02-05 20:33:32 -08:00
Peter Johnson
35aee1d78d [hal] Add ntcore to other libs in styleguide (NFC) (#7766) 2025-02-05 20:32:51 -08:00
DeltaDizzy
297f0d1b03 [wpiutil] Change kInvalidFile to macro (#7750)
This is needed on Windows because accessing global variables across shared library boundaries doesn’t work.
2025-01-30 20:01:35 -07:00
Thad House
ad29d45dfb [hal] Remove HAL_GetPort (#7754) 2025-01-30 19:59:34 -07:00
Thad House
6e704370b3 [hal, wpilib] Remove DigitalSource and AnalogTrigger (#7753) 2025-01-30 19:58:21 -07:00
Tyler Veness
7533b323d1 [upstream_utils] Upgrade to fmt 11.1.3 (#7629) 2025-01-30 13:35:33 -07:00
Thad House
48ce2dcc8d [hal, wpilib] Add initial systemcore counter implementation (#7723) 2025-01-28 09:58:34 -07:00
Thad House
b799b285b3 [hal, wpilib] Remove digital source from encoder (#7740) 2025-01-28 06:43:09 -07:00
PJ Reiniger
3b345fe218 [bazel] Add macros to build jni code and java tests (#7693) 2025-01-26 16:52:33 -08:00
Peter Johnson
eee30c49e2 [wpilib] Remove LiveWindow (#7733)
This will be replaced by a different mechanism, but removing it eases
the initial implementation burden of a new Telemetry/Sendable framework.
2025-01-25 10:52:19 -08:00
Peter Johnson
adbe95e610 [wpilib] Remove Shuffleboard API (#7730) 2025-01-24 23:47:42 -08:00
Peter Johnson
01e71e73ce [build] developerRobot: Fix link order (#7732)
ntcore now needs to be after hal.
2025-01-24 23:47:09 -08:00
Thad House
5898cdd5c3 [hal, wpilib] Remove interrupt (#7724) 2025-01-23 21:45:18 -08:00
Thad House
e2b6beb28a [hal, wpilib] Remove DigitalGlitchFilter (#7725) 2025-01-23 21:44:18 -08:00
Thad House
5a6c895b87 [hal, wpilib] Remove built in accelerometer (#7702) 2025-01-17 14:06:09 -08:00
Thad House
1600e773f4 [hal, wpilib] Remove DMA (#7701) 2025-01-17 14:05:34 -08:00
Thad House
f80874dd4b [hal, wpilib] Remove analog accumulator and analog gyro (#7697)
The 2 high level classes were temporarily kept to keep the examples compiling. We will remove those when we have the interface into the built in IMU.
2025-01-17 12:58:31 -08:00
Thad House
92f0a3c961 [hal, wpilib] Remove SPI support (#7678) 2025-01-17 00:22:29 -08:00
Thad House
dc335ddedb [hal] Remove everything that references chipobject or VISA (#7698) 2025-01-16 23:21:40 -08:00
Thad House
ff1b2a205e [hal, wpilib] Remove analog output (#7696) 2025-01-16 23:20:44 -08:00
Thad House
5017393b3a [hal, wpilib] Remove relay (#7695) 2025-01-16 23:20:07 -08:00
Peter Johnson
d9f8fded09 Merge branch 'main' into 2027 2025-01-16 23:17:59 -08:00
Thad House
1cad4f64a4 [hal] Add high level way of getting systemserver NT instance (#7683) 2025-01-16 10:49:40 -07:00
Thad House
58cb395d76 [hal] Add systemcore duty cycle (#7682) 2025-01-15 12:57:31 -07:00
Thad House
24d6e87447 Remove CrossConn and Integration Tests (#7692) 2025-01-15 12:53:17 -07:00
Thad House
f81c42e700 [hal] Fix systemcore analog input scaling (#7691) 2025-01-15 12:52:57 -07:00
Thad House
fa71fb55a2 [wpilib] Remove I2C Warnings (#7677)
SystemCore won't have the I2C issues.
2025-01-14 12:31:47 -07:00
Thad House
45d7549ca9 [hal] Add systemcore analog input (#7681) 2025-01-14 12:30:44 -07:00
Thad House
afbaa43539 [wpiutil] Remove roboRIO-specific timestamp code (#7669) 2025-01-13 15:59:11 -07:00
Thad House
e41b33960a [rtns] Remove roboRIO team number setter (#7667)
It won't be needed in 2027.
2025-01-13 12:26:03 -07:00
Thad House
df77580a15 [hal] Remove athena hal folder (#7668)
Also remove roborio bazel target.
2025-01-13 12:25:28 -07:00
Thad House
09a6bc9a25 [build] Remove RoboRIO specializations from build system (#7670) 2025-01-13 12:23:54 -07:00
Thad House
666d1638ce [hal] Digital IO SystemCore implementation (#7621) 2025-01-12 16:58:46 -08:00
Peter Johnson
03d9e96877 [wpiutil] Change StringExtras split() to template (#7636)
It now calls back a function for each part rather than creating a SmallVector.
2025-01-05 20:53:43 -08:00
Peter Johnson
0f6693594c [glass] Split DataSource into type-specific variants (#7588) 2025-01-03 13:36:40 -08:00
Peter Johnson
148fcdca85 [wpiutil] DataLog: Move schema info to a separate map (#7626) 2025-01-03 13:28:50 -08:00
Peter Johnson
93521420c8 [wpinet] uv::AddrToName: Add StringAssignable constraint (#7627) 2025-01-03 13:28:29 -08:00
Peter Johnson
12a1475ee4 [wpiutil] Remove LEB128 (#7628)
This was only used for NT3.
2025-01-03 07:13:49 -08:00
Peter Johnson
1240ee1bf4 [ntcore] Remove NT3 support (#7625)
- Remove StartClient3
- Rename StartClient4 to StartClient
- Remove port3 parameter from StartServer
- Remove 3-suffix constants
- Remove 4 suffix from constants

Also remove Shuffleboard build from CI.
2025-01-02 23:05:13 -08:00
Peter Johnson
da90ffd24a [wpical] Disable systemcore target (#7620) 2025-01-01 16:30:23 -08:00
Peter Johnson
a931a6554f Merge branch 'main' into 2027 2024-12-29 18:22:39 -08:00
Peter Johnson
3232630a38 Merge branch 'main' into 2027 2024-12-27 19:40:43 -08:00
Tyler Veness
df244cd198 [wpimath] Clean up arm and elevator feedforward APIs (#7595) 2024-12-27 08:12:14 -06:00
Peter Johnson
2a757eaeb5 Merge branch 'main' into 2027 2024-12-26 18:55:43 -08:00
hjelstromboli
78b14c5204 [hal] Update SmartIO PWM implementation (#7571)
* Integer microsecond setters and getters
* Per-port subtables
2024-12-22 13:45:44 -06:00
Peter Johnson
529bab6ca1 Merge branch 'main' into 2027 2024-12-19 20:40:37 -08:00
Tyler Veness
03f0fc4dea [wpimath] Use immutable member functions in ChassisSpeeds (#7545) 2024-12-15 16:09:34 -08:00
Peter Johnson
945d416d07 Merge branch 'main' into 2027 2024-12-14 10:45:32 -08:00
Thad House
6ba7189373 [hal] Enable periodic CAN sends (#7530)
* Implement periodic can support

* Fix build
2024-12-08 22:29:01 -08:00
Thad House
31d1aa62c1 [hal] Fixes for making PWM drive (#7528)
* Increase connection timeout, fix ld path, properly initialize smart io.

* Also fix HAL_GetSystemTimeValid, so DataLogManager doesn't throw errors.
2024-12-08 22:08:05 -08:00
Thad House
b6ae9e9cc9 Merge branch 'main' into 2027 2024-12-08 12:04:23 -08:00
Thad House
f1e4eafaa0 [hal] Add initial PWM support for systemcore (#7525) 2024-12-08 12:02:22 -08:00
Thad House
41d4826694 [hal] Initial CAN implementation for SystemCore (#7514) 2024-12-08 12:01:28 -08:00
Tyler Veness
220f4e1ba4 [wpimath] Remove RamseteController and RamseteCommand (#7522) 2024-12-07 23:38:35 -08:00
Tyler Veness
ae44295024 Use std::bit_cast (#7492) 2024-12-07 23:02:09 -08:00
Tyler Veness
4910436b10 [wpimath] Remove LUTs from LTV controllers (#7521)
The Raspberry Pi 5 is fast enough that we no longer need it.
```
Running ./build/DAREBench
Run on (4 X 2400 MHz CPU s)
CPU Caches:
  L1 Data 64 KiB (x4)
  L1 Instruction 64 KiB (x4)
  L2 Unified 512 KiB (x4)
  L3 Unified 2048 KiB (x1)
Load Average: 0.47, 0.72, 0.45
***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.
-------------------------------------------------------------------------------
Benchmark                                     Time             CPU   Iterations
-------------------------------------------------------------------------------
DARE_WPIMath_Dynamic                       34.4 us         34.4 us        20315
DARE_WPIMath_NoPrecondChecks_Dynamic       21.7 us         21.7 us        32266
DARE_WPIMath_Static                        15.2 us         15.2 us        45878
DARE_WPIMath_NoPrecondChecks_Static        7.84 us         7.84 us        89316
DARE_SLICOT                                79.4 us         79.4 us         8789
DARE_Drake                                 34.9 us         34.9 us        20074
```
2024-12-07 23:00:15 -08:00
Tyler Veness
a7349f00ef [wpimath] Fix duplicate Rotation2d constructor (#7524) 2024-12-07 22:31:09 -08:00
Peter Johnson
e493da3486 Merge branch 'main' into 2027 2024-12-07 21:38:18 -08:00
Peter Johnson
32ba751e58 Merge branch 'main' into 2027 2024-12-07 21:30:28 -08:00
Tyler Veness
62a6a77bbf [wpimath] Add affine transformation constructors and getters to geometry API (#7509)
Fixes #7429.
2024-12-07 21:29:02 -08:00
Tyler Veness
c81bd0c909 [ci] Upgrade to Ubuntu 24.04 (#7496) 2024-12-07 21:20:48 -08:00
Tyler Veness
c497e4ec22 [wpimath] Fix SimpleFeedforward overload set (#7516) 2024-12-07 20:32:16 -08:00
Thad House
6dbff902fa [build] Remove athena completely from build (#7517) 2024-12-07 20:31:10 -08:00
Thad House
be72e0ecd8 Skip linux arm64 build on tools build (#7511) 2024-12-07 16:37:11 -08:00
Thad House
e69c5710b3 [hal] Add ErrorInfo support to systemcore DS (#7488) 2024-12-07 14:14:21 -08:00
Peter Johnson
52b33edcbd Merge branch 'main' into 2027 2024-12-07 14:11:47 -08:00
Thad House
c8900cadc3 Add SPARKmini to PWM support (#7504) 2024-12-07 00:48:20 -08:00
Thad House
5058b48dea [developerRobot] Switch to initial password and host for systemcore (#7503) 2024-12-06 23:19:48 -08:00
Tyler Veness
144e79a614 [wpimath] Remove Rotation2d value field (#7490)
It's not part of SO(2).
2024-12-06 21:00:09 -08:00
Thad House
38b09a6dfd [hal] Clean up systemcore notifier impl (#7487)
* Clean up systemcore notifier impl

* Formatting fixes

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-05 09:17:45 -08:00
Thad House
d7cd71589a [hal] Enable an I2C bus on systemcore (#7485) 2024-12-04 20:19:51 -08:00
Thad House
a954091ea2 [ci] Remove 32-bit Windows builds (#7475) 2024-12-01 17:45:04 -08:00
Thad House
bf653d9895 [hal] Add SystemServer DS support (#7466) 2024-12-01 17:05:07 -08:00
Thad House
5a9e0abe44 [ci] Build tools with systemcore image (#7472) 2024-12-01 17:03:51 -08:00
Peter Johnson
5cab27fdd5 Merge branch 'main' into 2027 2024-12-01 17:03:03 -08:00
Thad House
ce63770970 Merge branch 'main' into 2027 2024-12-01 12:40:50 -08:00
Thad House
c51f65bd4f [hal] Add initial SystemServer support (#7463) 2024-11-30 20:31:26 -08:00
Thad House
82132c3272 [hal] Initial SystemCore empty HAL (#7454) 2024-11-30 10:04:00 -08:00
Peter Johnson
847c3120d3 Merge branch 'main' into 2027 2024-11-30 00:35:00 -08:00
Peter Johnson
7ae4333c81 [ci] Publish on 2027 repositories 2024-11-29 23:52:22 -08:00
2052 changed files with 41177 additions and 102964 deletions

View File

@@ -3,6 +3,8 @@ try-import %workspace%/user.bazelrc
common --noenable_bzlmod
build --incompatible_disallow_empty_glob=1 # Bazel 8 prep
build --java_language_version=17
build --java_runtime_version=roboriojdk_17
build --tool_java_language_version=17
@@ -11,14 +13,14 @@ 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/systemcore_flags.rc
import shared/bazel/compiler_flags/windows_flags.rc
import shared/bazel/compiler_flags/coverage_flags.rc
import %workspace%/shared/bazel/compiler_flags/sanitizers.rc
import %workspace%/shared/bazel/compiler_flags/base_linux_flags.rc
import %workspace%/shared/bazel/compiler_flags/linux_flags.rc
import %workspace%/shared/bazel/compiler_flags/osx_flags.rc
import %workspace%/shared/bazel/compiler_flags/roborio_flags.rc
import %workspace%/shared/bazel/compiler_flags/systemcore_flags.rc
import %workspace%/shared/bazel/compiler_flags/windows_flags.rc
import %workspace%/shared/bazel/compiler_flags/coverage_flags.rc
# Alias toolchain names to what wpilibsuite uses for CI/Artifact naming
build:athena --config=roborio

View File

@@ -18,9 +18,6 @@ runs:
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
shell: bash
- name: Regenerate hal
run: ./hal/generate_usage_reporting.py
shell: bash
- name: Regenerate ntcore
run: ./ntcore/generate_topics.py

3
.github/labeler.yml vendored
View File

@@ -33,9 +33,6 @@
'component: sysid':
- changed-files:
- any-glob-to-any-file: sysid/**
'component: teamnumbersetter':
- changed-files:
- any-glob-to-any-file: roborioteamnumbersetter/**
'component: wpilibc':
- changed-files:
- any-glob-to-any-file: wpilibc/**

View File

@@ -31,8 +31,8 @@ jobs:
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
- name: bazel ${{ matrix.action }}
run: bazel --output_user_root=C:\\bazelroot ${{ matrix.action }} -k ... --config=ci ${{ matrix.config }} --verbose_failures
shell: bash
build-mac:
@@ -47,7 +47,7 @@ jobs:
with:
token: ${{ secrets.BUILDBUDDY_API_KEY }}
- name: Build Release
- name: bazel test (release)
run: bazel test -k ... --config=ci -c opt --config=macos --nojava_header_compilation --verbose_failures
shell: bash
@@ -56,10 +56,11 @@ jobs:
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: "Linux (native)", os: ubuntu-24.04, container: "wpilib/ubuntu-base:22.04", action: "test", config: "--config=linux", }
- { name: "Linux (systemcore)", os: ubuntu-24.04, container: "wpilib/ubuntu-base:22.04", action: "build", config: "--config=systemcore", }
name: "${{ matrix.name }}"
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
@@ -70,12 +71,12 @@ jobs:
with:
token: ${{ secrets.BUILDBUDDY_API_KEY }}
- name: Build and Test Release
- name: bazel ${{ matrix.action }} (release)
run: bazel ${{ matrix.action }} ... --config=ci -c opt ${{ matrix.config }} -k --verbose_failures
buildifier:
name: "buildifier"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Set up Go 1.15.x
uses: actions/setup-go@v5

View File

@@ -16,10 +16,10 @@ jobs:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
- os: ubuntu-24.04
name: Android Arm64
abi: arm64-v8a
- os: ubuntu-22.04
- os: ubuntu-24.04
name: Android X64
abi: "x86_64"
@@ -39,7 +39,7 @@ jobs:
java-version: 17
- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.5
uses: mozilla-actions/sccache-action@v0.0.9
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install -y ninja-build

View File

@@ -16,9 +16,9 @@ jobs:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
- os: ubuntu-24.04
name: Linux
container: wpilib/roborio-cross-ubuntu:2025-22.04
container: wpilib/roborio-cross-ubuntu:2025-24.04
flags: "--preset with-java-and-sccache -DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON"
- os: macOS-14
name: macOS
@@ -36,7 +36,7 @@ jobs:
steps:
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java libprotobuf-dev protobuf-compiler ninja-build
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv-java libprotobuf-dev protobuf-compiler ninja-build
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
@@ -50,7 +50,7 @@ jobs:
uses: lukka/get-cmake@v3.29.3
- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.5
uses: mozilla-actions/sccache-action@v0.0.9
- uses: actions/checkout@v4

View File

@@ -12,8 +12,8 @@ env:
jobs:
publish:
name: "Documentation - Publish"
runs-on: ubuntu-22.04
if: github.repository == 'wpilibsuite/allwpilib' && (github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
runs-on: ubuntu-24.04
if: github.repository == 'wpilibsuite/allwpilib' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
concurrency: ci-docs-publish
steps:
- uses: actions/checkout@v4
@@ -39,6 +39,11 @@ jobs:
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=release" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, '2027')
- name: Set environment variables (2027)
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=2027" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '2027')
- name: Build with Gradle
run: ./gradlew docs:generateJavaDocs docs:doxygen -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
- name: Install SSH Client 🔑

View File

@@ -19,20 +19,20 @@ jobs:
fail-fast: false
matrix:
include:
- container: wpilib/roborio-cross-ubuntu:2025-22.04
artifact-name: Athena
build-options: "-Ponlylinuxathena"
- container: wpilib/raspbian-cross-ubuntu:bookworm-22.04
- container: wpilib/systemcore-cross-ubuntu:2025-24.04
artifact-name: SystemCore
build-options: "-Ponlylinuxsystemcore"
- container: wpilib/raspbian-cross-ubuntu:bookworm-24.04
artifact-name: Arm32
build-options: "-Ponlylinuxarm32"
- container: wpilib/aarch64-cross-ubuntu:bookworm-22.04
- container: wpilib/aarch64-cross-ubuntu:bookworm-24.04
artifact-name: Arm64
build-options: "-Ponlylinuxarm64"
- container: wpilib/ubuntu-base:22.04
- container: wpilib/ubuntu-base:24.04
artifact-name: Linux
build-options: "-Ponlylinuxx86-64"
name: "Build - ${{ matrix.artifact-name }}"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
needs: [validation]
steps:
- name: Free Disk Space
@@ -50,7 +50,7 @@ jobs:
fetch-depth: 0
- name: Set release environment variable
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
if: startsWith(github.ref, 'refs/tags/v2027')
- name: Build with Gradle
uses: addnab/docker-run-action@v3
with:
@@ -103,12 +103,6 @@ jobs:
architecture: aarch64
task: "build"
outputs: "build/allOutputs"
- os: windows-2022
artifact-name: Win32FFI
architecture: x86
task: ":ntcoreffi:build"
build-options: "-Pntcoreffibuild \"-Dorg.gradle.jvmargs=-Xmx1096m\""
outputs: "ntcoreffi/build/outputs"
- os: windows-2022
artifact-name: Win64FFI
architecture: x64
@@ -135,19 +129,16 @@ jobs:
keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
if: |
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027'))))
(github.ref == 'refs/heads/2027' || startsWith(github.ref, 'refs/tags/v2027')))
- name: Set Keychain Lock Timeout
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') && !contains(github.ref, '2027'))))
(github.ref == 'refs/heads/2027' || startsWith(github.ref, 'refs/tags/v2027')))
- name: Set release environment variable
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
shell: bash
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Set Java Heap Size
run: sed -i 's/-Xmx2g/-Xmx1g/g' gradle.properties
if: matrix.artifact-name == 'Win32'
if: startsWith(github.ref, 'refs/tags/v2027')
- name: Check disk free space (Windows)
run: wmic logicaldisk get caption, freespace
if: matrix.os == 'windows-2022'
@@ -175,7 +166,7 @@ jobs:
run: ./gradlew copyAllOutputs --build-cache -PbuildServer -PskipJavaFormat -PdeveloperID=${{ secrets.APPLE_DEVELOPER_ID }} ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
if: |
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027'))))
(github.ref == 'refs/heads/2027' || startsWith(github.ref, 'refs/tags/v2027')))
- name: Check disk free space (Windows)
run: wmic logicaldisk get caption, freespace
if: matrix.os == 'windows-2022'
@@ -189,7 +180,7 @@ jobs:
build-documentation:
name: "Build - Documentation"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
needs: [validation]
steps:
- uses: actions/checkout@v4
@@ -201,7 +192,7 @@ jobs:
java-version: 17
- name: Set release environment variable
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
if: startsWith(github.ref, 'refs/tags/v2027')
- name: Build with Gradle
run: ./gradlew docs:zipDocs --build-cache -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
env:
@@ -215,12 +206,12 @@ jobs:
combine:
name: Combine
needs: [build-docker, build-host, build-documentation]
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Free Disk Space
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
(github.ref == 'refs/heads/2027' || startsWith(github.ref, 'refs/tags/v2027'))
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
@@ -233,48 +224,48 @@ jobs:
- uses: actions/checkout@v4
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
(github.ref == 'refs/heads/2027' || startsWith(github.ref, 'refs/tags/v2027'))
with:
repository: wpilibsuite/build-tools
- uses: actions/download-artifact@v4
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
(github.ref == 'refs/heads/2027' || startsWith(github.ref, 'refs/tags/v2027'))
with:
path: combiner/products/build/allOutputs
- name: Flatten Artifacts
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
(github.ref == 'refs/heads/2027' || startsWith(github.ref, 'refs/tags/v2027'))
run: rsync -a --delete combiner/products/build/allOutputs/*/* combiner/products/build/allOutputs/
- name: Check version number exists
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
(github.ref == 'refs/heads/2027' || startsWith(github.ref, 'refs/tags/v2027'))
run: |
cat combiner/products/build/allOutputs/version.txt
test -s combiner/products/build/allOutputs/version.txt
- uses: actions/setup-java@v4
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
(github.ref == 'refs/heads/2027' || startsWith(github.ref, 'refs/tags/v2027'))
with:
distribution: 'temurin'
java-version: 17
- name: Combine (Main)
- name: Combine (2027)
if: |
github.repository == 'wpilibsuite/allwpilib' &&
github.ref == 'refs/heads/main'
run: cd combiner && ./gradlew publish -Pallwpilib
github.ref == 'refs/heads/2027'
run: cd combiner && ./gradlew publish -Pallwpilib -Pbuild2027
env:
RUN_AZURE_ARTIFACTORY_RELEASE: "TRUE"
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
- name: Combine (Release)
- name: Combine (2027 Release)
if: |
github.repository == 'wpilibsuite/allwpilib' &&
startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
run: cd combiner && ./gradlew publish -Pallwpilib -PreleaseRepoPublish
startsWith(github.ref, 'refs/tags/v2027')
run: cd combiner && ./gradlew publish -Pallwpilib -PreleaseRepoPublish -Pbuild2027
env:
RUN_AZURE_ARTIFACTORY_RELEASE: "TRUE"
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
@@ -282,25 +273,7 @@ jobs:
- uses: actions/upload-artifact@v4
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
(github.ref == 'refs/heads/2027' || startsWith(github.ref, 'refs/tags/v2027'))
with:
name: Maven
path: ~/releases
dispatch:
name: dispatch
needs: [combine]
strategy:
matrix:
repo: ['SmartDashboard', 'PathWeaver', 'Shuffleboard', 'RobotBuilder']
runs-on: ubuntu-22.04
steps:
- uses: peter-evans/repository-dispatch@v3
if: |
github.repository == 'wpilibsuite/allwpilib' &&
startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
with:
token: ${{ secrets.TOOL_REPO_ACCESS_TOKEN }}
repository: wpilibsuite/${{ matrix.repo }}
event-type: tag
client-payload: '{"package_name": "allwpilib", "package_version": "${{ github.ref_name }}"}'

View File

@@ -20,7 +20,7 @@ jobs:
wpiformat:
name: "wpiformat"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
@@ -36,7 +36,7 @@ jobs:
- name: Install wpiformat
run: |
python -m venv ${{ runner.temp }}/wpiformat
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2024.51
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.33
- name: Run
run: ${{ runner.temp }}/wpiformat/bin/wpiformat
- name: Check output
@@ -59,9 +59,9 @@ jobs:
tidy:
name: "clang-tidy"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
needs: [validation]
container: wpilib/ubuntu-base:22.04
container: wpilib/ubuntu-base:24.04
steps:
- uses: actions/checkout@v4
with:
@@ -78,7 +78,7 @@ jobs:
- name: Install wpiformat
run: |
python -m venv ${{ runner.temp }}/wpiformat
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2024.51
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.33
- name: Create compile_commands.json
run: |
./gradlew generateCompileCommands -Ptoolchain-optional-roboRio
@@ -92,9 +92,9 @@ jobs:
run: ${{ runner.temp }}/wpiformat/bin/wpiformat -no-format -tidy-changed -compile-commands=build/TargetedCompileCommands/linuxx86-64debug -vv
javaformat:
name: "Java format"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
needs: [validation]
container: wpilib/ubuntu-base:22.04
container: wpilib/ubuntu-base:24.04
steps:
- uses: actions/checkout@v4
with:
@@ -126,7 +126,7 @@ jobs:
documentation:
name: "Documentation"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
needs: [validation]
steps:
- uses: actions/checkout@v4

View File

@@ -13,7 +13,7 @@ concurrency:
jobs:
update:
name: "Update"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:

View File

@@ -33,15 +33,15 @@ jobs:
container: wpilib/roborio-cross-ubuntu:2025-24.04
steps:
- name: Install Dependencies
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv-java clang-17 libprotobuf-dev protobuf-compiler ninja-build
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv-java clang-18 libprotobuf-dev protobuf-compiler ninja-build
- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.5
uses: mozilla-actions/sccache-action@v0.0.9
- uses: actions/checkout@v4
- name: configure
run: mkdir build && cd build && cmake -G Ninja -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/clang-17 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-17 -DWITH_JAVA=OFF ${{ matrix.cmake-flags }} ..
run: mkdir build && cd build && cmake -G Ninja -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/clang-18 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-18 -DWITH_JAVA=OFF ${{ matrix.cmake-flags }} ..
env:
SCCACHE_WEBDAV_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
SCCACHE_WEBDAV_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}

View File

@@ -23,20 +23,20 @@ jobs:
fail-fast: false
matrix:
include:
- container: wpilib/roborio-cross-ubuntu:2025-22.04
- container: wpilib/roborio-cross-ubuntu:2025-24.04
artifact-name: Athena
build-options: "-Ponlylinuxathena"
- container: wpilib/raspbian-cross-ubuntu:bookworm-22.04
- container: wpilib/raspbian-cross-ubuntu:bookworm-24.04
artifact-name: Arm32
build-options: "-Ponlylinuxarm32"
- container: wpilib/aarch64-cross-ubuntu:bookworm-22.04
- container: wpilib/aarch64-cross-ubuntu:bookworm-24.04
artifact-name: Arm64
build-options: "-Ponlylinuxarm64"
- container: wpilib/ubuntu-base:22.04
- container: wpilib/ubuntu-base:24.04
artifact-name: Linux
build-options: "-Ponlylinuxx86-64"
name: "Build - ${{ matrix.artifact-name }}"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
needs: [validation]
steps:
- name: Free Disk Space
@@ -102,11 +102,6 @@ jobs:
architecture: aarch64
task: "build"
outputs: "build/allOutputs"
- os: windows-2022
artifact-name: Win32
architecture: x86
task: ":ntcoreffi:build"
outputs: "ntcoreffi/build/outputs"
name: "Build - ${{ matrix.artifact-name }}"
runs-on: ${{ matrix.os }}
needs: [validation]
@@ -131,9 +126,6 @@ jobs:
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
run: sed -i 's/-Xmx2g/-Xmx1g/g' gradle.properties
if: matrix.artifact-name == 'Win32'
- name: Check disk free space (Windows)
run: wmic logicaldisk get caption, freespace
if: matrix.os == 'windows-2022'

View File

@@ -12,7 +12,7 @@ env:
jobs:
build-artifacts:
name: "Build - WPILib"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
env:
DISPLAY: ':10'
steps:
@@ -33,174 +33,12 @@ jobs:
- name: Build WPILib with Gradle
uses: addnab/docker-run-action@v3
with:
image: wpilib/roborio-cross-ubuntu:2025-22.04
image: wpilib/systemcore-cross-ubuntu:2025-24.04
options: -v ${{ github.workspace }}:/work -w /work -e GITHUB_REF -e CI -e DISPLAY
run: df . && rm -f semicolon_delimited_script && ./gradlew :wpilibc:publish :wpilibj:publish :wpilibNewCommands:publish :hal:publish :cameraserver:publish :ntcore:publish :cscore:publish :wpimath:publish :wpinet:publish :wpiutil:publish :apriltag:publish :wpiunits:publish :simulation:halsim_gui:publish :simulation:halsim_ds_socket:publish :simulation:halsim_ws_server:publish :simulation:halsim_ws_client:publish :simulation:halsim_xrp:publish :fieldImages:publish :romiVendordep:publish :xrpVendordep:publish :epilogue-processor:publish :epilogue-runtime:publish :thirdparty:googletest:publish -x test -x Javadoc -x doxygen --build-cache && cp -r /root/releases/maven/development /work
run: df . && rm -f semicolon_delimited_script && ./gradlew -Pskiplinuxarm64 :wpilibc:publish :wpilibj:publish :wpilibNewCommands:publish :hal:publish :cameraserver:publish :ntcore:publish :cscore:publish :wpimath:publish :wpinet:publish :wpiutil:publish :apriltag:publish :wpiunits:publish :simulation:halsim_gui:publish :simulation:halsim_ds_socket:publish :fieldImages:publish :epilogue-processor:publish :epilogue-runtime:publish :thirdparty:googletest:publish -x test -x Javadoc -x doxygen --build-cache && cp -r /root/releases/maven/development /work
- uses: actions/upload-artifact@v4
with:
name: MavenArtifacts
path: |
development
retention-days: 1
Robotbuilder:
name: "Build - RobotBuilder"
needs: [build-artifacts]
runs-on: ubuntu-22.04
env:
DISPLAY: ':10'
steps:
- uses: actions/checkout@v4
with:
repository: wpilibsuite/robotbuilder
fetch-depth: 0
- uses: actions/download-artifact@v4
with:
name: MavenArtifacts
- name: Patch RobotBuilder to use local development
run: cd src/main/resources/export && echo "wpi.maven.useLocal = false" >> java/build.gradle && echo "wpi.maven.useFrcMavenLocalDevelopment = true" >> java/build.gradle && echo "wpi.versions.wpilibVersion = '$YEAR.424242.+'" >> java/build.gradle && echo "wpi.versions.wpimathVersion = '$YEAR.424242.+'" >> java/build.gradle && echo "wpi.maven.useLocal = false" >> cpp/build.gradle && echo "wpi.maven.useFrcMavenLocalDevelopment = true" >> cpp/build.gradle && echo "wpi.versions.wpilibVersion = '$YEAR.424242.+'" >> cpp/build.gradle && echo "wpi.versions.wpimathVersion = '$YEAR.424242.+'" >> cpp/build.gradle
- name: Install and run xvfb
run: sudo apt-get update && sudo apt-get install -y xvfb && Xvfb $DISPLAY &
- name: Move artifacts
run: mkdir -p ~/releases/maven/development && cp -r edu ~/releases/maven/development
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'
- name: Build RobotBuilder with Gradle
run: ./gradlew build test --tests 'robotbuilder.exporters.*' -x htmlSanityCheck -PbuildServer -PreleaseMode ; cat build/test-results/test/TEST-robotbuilder.exporters.*.xml ;
- name: Summarize RobotBuilder Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: |
build/test-results/test/TEST*.xml
check_run: false
comment_mode: off
- uses: actions/upload-artifact@v4
if: always()
with:
name: RobotBuilderTestResults
path: |
build/reports/
- uses: actions/upload-artifact@v4
with:
name: RobotBuilder Build
path: |
build/libs/
retention-days: 7
Shuffleboard:
name: "Build - Shuffleboard"
needs: [build-artifacts]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
repository: wpilibsuite/shuffleboard
fetch-depth: 0
- name: Patch Shuffleboard to use local development
run: sed -i "s/wpilibTools.deps.wpilibVersion.*/wpilibTools.deps.wpilibVersion = \'$YEAR\.424242\.+\'/" app/app.gradle && sed -i "s/wpilibTools.deps.wpilibVersion.*/wpilibTools.deps.wpilibVersion = \'$YEAR\.424242\.+\'/" plugins/cameraserver/cameraserver.gradle && sed -i "s/wpilibTools.deps.wpilibVersion.*/wpilibTools.deps.wpilibVersion = \'$YEAR\.424242\.+\'/" plugins/networktables/networktables.gradle
- uses: actions/download-artifact@v4
with:
name: MavenArtifacts
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'
- name: Move artifacts
run: mkdir -p ~/releases/maven/development && cp -r edu ~/releases/maven/development
- name: Install dependencies
run: sudo apt-get install -y libgtk2.0-0
- name: Build with Gradle
run: ./gradlew build -x Javadoc
- uses: actions/upload-artifact@v4
with:
name: Shuffleboard Build
path: |
build/allOutputs/
retention-days: 7
PathWeaver:
name: "Build - PathWeaver"
needs: [build-artifacts]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
repository: wpilibsuite/PathWeaver
fetch-depth: 0
- name: Patch PathWeaver to use local development
run: sed -i "s/wpilibTools.deps.wpilibVersion.*/wpilibTools.deps.wpilibVersion = \'$YEAR\.424242\.+\'/" dependencies.gradle
- uses: actions/download-artifact@v4
with:
name: MavenArtifacts
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'
- name: Move artifacts
run: mkdir -p ~/releases/maven/development && cp -r edu ~/releases/maven/development
- name: Build with Gradle
run: ./gradlew build
- uses: actions/upload-artifact@v4
with:
name: PathWeaver Build
path: |
build/allOutputs/
retention-days: 7
Robotpy:
name: "Build - Robotpy"
needs: [build-artifacts]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
repository: robotpy/mostrobotpy
fetch-depth: 0
- uses: actions/download-artifact@v4
with:
name: MavenArtifacts
- name: Move artifacts
run: mkdir -p ~/releases/maven/development && cp -r edu ~/releases/maven/development
- uses: actions/setup-python@v5
with:
python-version: 3.13
#
# Setup build caching
#
- name: Set ccache size
shell: bash
id: ccache
run: echo "MAX_SIZE=500M" >> $GITHUB_OUTPUT
- name: Setup ccache
# uses: hendrikmuhs/ccache-action@v1.2.10
uses: robotpy/ccache-action@fork
with:
key: ubuntu-22.04-3.13
variant: ccache
max-size: ${{ steps.ccache.outputs.max_size }}
- name: Install deps
shell: bash
run: |
python -m pip --disable-pip-version-check install -r rdev_requirements.txt
- name: Install numpy (needed for stubgen but broken in raspbian CI)
shell: bash
run: |
python -m pip --disable-pip-version-check install numpy
- name: Patch RobotPy rdev to use local development
run: git config user.name github-actions && git config user.email github-actions@github.com && set -- ~/releases/maven/development/edu/wpi/first/wpiutil/wpiutil-cpp/*/ ; wpilibversion=$(basename $1) && echo $wpilibversion && sed --regexp-extended -i 's@(wpilib_bin_url =).*@\1 \"file:\/\/'"$HOME"'\/releases\/maven\/development"@' rdev.toml && sed --regexp-extended -i 's/(wpilib_bin_version =).*/\1 \"'"$wpilibversion"'\"/' rdev.toml && ./rdev.sh update-pyproject --commit
- name: Build + test wheels
shell: bash
run: |
./rdev.sh ci run
env:
RPYBUILD_STRIP_LIBPYTHON: "1"
RPYBUILD_CC_LAUNCHER: ccache

View File

@@ -13,7 +13,7 @@ concurrency:
jobs:
update:
name: "Update"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:

View File

@@ -14,6 +14,7 @@ modifiableFileExclude {
thirdparty/
\.patch$
gradlew
BUILD.bazel
}
generatedFileExclude {

50
BUILD.bazel Normal file
View File

@@ -0,0 +1,50 @@
load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files")
load("@rules_python//python:pip.bzl", "compile_pip_requirements")
filegroup(
name = "license",
srcs = [
"LICENSE.md",
"ThirdPartyNotices.txt",
],
visibility = ["//visibility:public"],
)
# bazel build //:requirements.lock
compile_pip_requirements(
name = "requirements",
extra_args = ["--allow-unsafe"],
requirements_in = "requirements.txt",
requirements_txt = "requirements_lock.txt",
# compile_pip_requirements does not respect target_compatible_with for some of the targets it generates under the hood
tags = ["no-systemcore"],
)
alias(
name = "quickbuf_protoc",
actual = select({
"@bazel_tools//src/conditions:darwin": "@quickbuffer_protoc_osx//file",
"@bazel_tools//src/conditions:windows": "@quickbuffer_protoc_windows//file",
"@rules_bzlmodrio_toolchains//constraints/combined:is_linux": "@quickbuffer_protoc_linux//file",
}),
tags = ["pregeneration"],
visibility = ["//visibility:public"],
)
# This is a helper to run all of the pregeneration scripts at once.
write_source_files(
name = "write_all",
additional_update_targets = [
"//hal:write_hal",
"//ntcore:write_ntcore",
"//wpilibc:write_wpilibc",
"//wpilibcExamples:write_example_project_list",
"//wpilibj:write_wpilibj",
"//wpilibjExamples:write_example_project_list",
"//wpilibNewCommands:write_wpilib_new_commands",
"//wpimath:write_wpimath",
"//wpiunits:write_wpiunits",
"//wpiutil:write_wpiutil",
],
tags = ["pregeneration"],
)

View File

@@ -291,6 +291,8 @@ set(WPIUNITS_DEP_REPLACE_IMPL "find_dependency(wpiunits)")
set(WPIUTIL_DEP_REPLACE "find_dependency(wpiutil)")
add_subdirectory(wpiutil)
add_subdirectory(datalog)
if(WITH_NTCORE)
set(NTCORE_DEP_REPLACE "find_dependency(ntcore)")
set(WPINET_DEP_REPLACE "find_dependency(wpinet)")
@@ -324,7 +326,6 @@ if(WITH_GUI)
add_subdirectory(wpical)
endif()
if(LIBSSH_FOUND)
add_subdirectory(roborioteamnumbersetter)
add_subdirectory(datalogtool)
endif()
endif()

View File

@@ -14,54 +14,27 @@ The development repository is where development releases of every commit to [mai
## Artifact classifiers
We provide two base types of artifacts.
The first types are Java artifacts. These are usually published as `jar` files. Usually, the actual jar file is published with no classifier. The sources are published with the `-sources` classifier, and the javadocs are published with the `-javadoc` classifier.
The second types are native artifacts. These are usually published as `zip` files (except for the `JNI` artifact types, which are `jar` files. See below for information on this). The `-sources` and `-headers` classifiers contain the sources and headers respectively for the library. Each artifact also contains a classifier for each platform we publish. This platform is in the format `{os}{arch}`. The platform artifact only contains the binaries for a specific platform. In addition, we provide a `-all` classifier. This classifier combines all of the platform artifacts into a single artifact. This is useful for tools that cannot determine what version to use during builds. However, we recommend using the platform specific classifier when possible. Note that the binary artifacts never contain the headers, you always need the `-headers` classifier to get those.
## Artifact Names
WPILib builds four different types of artifacts.
##### C++ Only Libraries
When we publish C++ only libraries, they are published with the base artifact name as their artifact name, with a `-cpp` extension. All dependencies for the library are linked as shared libraries to the binary.
Example:
```
edu.wpi.first.wpilibc:wpilibc-cpp:version:classifier@zip
```
#### Java Only Libraries
When we publish Java only libraries, they are published with the base artifact name as their artifact name, with a `-java` extension.
The first types are Java artifacts. These are usually published as `jar` files. Usually, the actual jar file is published with no classifier. The sources are published with the `-sources` classifier, and the javadocs are published with the `-javadoc` classifier. These artifacts are published with the base artifact name as their artifact ID, with a `-java` extension.
Example:
```
edu.wpi.first.wpilibj:wpilibj-java:version
```
#### C++/Java Libraries without JNI
For libraries that are both C++ and Java, but without a JNI component, the C++ component is published with the `basename-cpp` artifact name, and the Java component is published with the `basename-java` artifact name.
The second types are native artifacts. These are usually published as `zip` files. The `-sources` and `-headers` classifiers contain the sources and headers respectively for the library. Each artifact also contains a classifier for each platform we publish. This platform is in the format `{os}{arch}`. The full list of supported platforms can be found in [native-utils](https://github.com/wpilibsuite/native-utils/blob/main/src/main/java/edu/wpi/first/nativeutils/WPINativeUtilsExtension.java#L94). If the library is built statically, it will have `static` appended to the classifier. Additionally, if the library was built in debug mode, `debug` will be appended to the classifier. The platform artifact only contains the binaries for a specific platform. Note that the binary artifacts never contain the headers, you always need the `-headers` classifier to get those.
If the library is Java and C++ and has a JNI component, the native artifact will have a shared library containing JNI entrypoints alongside the C++ shared library. This JNI shared library will have a `jni` suffix in the file name.
Native artifacts are published with the base artifact name as their artifact ID, with a `-cpp` extension.
Example:
```
edu.wpi.first.wpiutil:wpiutil-cpp:version:classifier@zip (C++)
edu.wpi.first.wpiutil:wpiutil-java:version (Java)
```
#### C++/Java Libraries with JNI
For libraries that are both C++ and Java with a JNI component there are three different artifact names. For Java, the component is published as `basename-java`. For C++, the `basename-cpp` artifact contains the C++ artifacts with all dependencies linked as shared libraries to the binary. These binaries DO contain the JNI entry points. The `basename-jni` artifact contains identical C++ binaries to the `-cpp` artifact, however all of its dependencies are statically linked, and only the JNI and C entry points are exported.
The `-jni` artifact should only be used in cases where you want to create a self contained Java application where the native artifacts are embedded in the jar. Note in an extraction scenario, extending off of the library is never supported, which is why the C++ entry points are not exposed. The name of the library is randomly generated during extraction. For pretty much all cases, and if you ever want to extend from a native library, you should use the `-cpp` artifacts. GradleRIO uses the `-cpp` artifacts for all platforms, even desktop, for this reason.
Example:
```
edu.wpi.first.ntcore:ntcore-cpp:version:classifier@zip (C++)
edu.wpi.first.ntcore:ntcore-jni:version:classifier (JNI jar library)
edu.wpi.first.ntcore:ntcore-java:version (Java)
edu.wpi.first.wpimath:wpimath-cpp:version:classifier@zip
edu.wpi.first.wpimath:wpimath-cpp:version:windowsx86-64staticdebug@zip
```
## Provided Artifacts
This repository provides the following artifacts. Below each artifact is its dependencies. Note if ever using the `-jni` artifacts, no dependencies are needed for native binaries.
This repository provides the following artifacts. Below each artifact is its dependencies.
For C++, if building with static dependencies, the listed order should be the link order in your linker.

View File

@@ -23,3 +23,9 @@ Examples:
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
```
## Pregenerating Files
allwpilib uses extensive use of pre-generating files that are later used to build C++ / Java libraries that are tracked by version control. Quite often,
these pre-generation scripts use some configuration file to create multipile files inside of an output directory. While this process could be accomplished
with a `genrule` that would require an explicit listing of every output file, which would be tedious to maintain as well as potentially confusing to people
adding new features those libraries. Therefor, we use `@aspect_bazel_lib` and their `write_source_files` feature to generate these directories. In the event that the generation process creates more than a small handful of predictable files, a custom rule is written to generate the directory.

View File

@@ -24,7 +24,6 @@ WPILib is normally built with Gradle, however for some systems, such as Linux ba
* datalogtool
* glass
* outlineviewer
* roborioteamnumbersetter
* sysid
* halsim_gui (if simulation extensions are enabled)

View File

@@ -50,7 +50,7 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
- C++ compiler
- On Linux, install GCC 11 or greater
- On Windows, install [Visual Studio Community 2022](https://visualstudio.microsoft.com/vs/community/) and select the C++ programming language during installation (Gradle can't use the build tools for Visual Studio)
- On macOS, install the Xcode command-line build tools via `xcode-select --install`. Xcode 13 or later is required.
- On macOS 13.3 or newer, install Xcode 14 or later (the command-line build tools are insufficient).
- ARM compiler toolchain
- Run `./gradlew installRoboRioToolchain` after cloning this repository
- If the WPILib installer was used, this toolchain is already installed

171
WORKSPACE
View File

@@ -1,4 +1,34 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
# Rules Python
http_archive(
name = "rules_python",
sha256 = "c68bdc4fbec25de5b5493b8819cfc877c4ea299c0dcb15c244c5a00208cde311",
strip_prefix = "rules_python-0.31.0",
url = "https://github.com/bazelbuild/rules_python/releases/download/0.31.0/rules_python-0.31.0.tar.gz",
)
load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains")
py_repositories()
python_register_toolchains(
name = "python_3_10",
ignore_root_user_error = True,
python_version = "3.10",
)
load("@rules_python//python:pip.bzl", "pip_parse")
pip_parse(
name = "allwpilib_pip_deps",
python_interpreter_target = "@python_3_10_host//:python",
requirements_lock = "//:requirements_lock.txt",
)
load("@allwpilib_pip_deps//:requirements.bzl", "install_deps")
install_deps()
# Download Extra java rules
http_archive(
@@ -13,6 +43,7 @@ load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps")
rules_jvm_external_deps()
load("@rules_jvm_external//:defs.bzl", "maven_install")
load("@rules_jvm_external//:specs.bzl", "maven")
maven_artifacts = [
"org.ejml:ejml-simple:0.43.1",
@@ -21,17 +52,100 @@ maven_artifacts = [
"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.artifact(
"org.junit.jupiter",
"junit-jupiter",
"5.10.1",
testonly = True,
),
maven.artifact(
"org.junit.platform",
"junit-platform-console",
"1.10.1",
testonly = True,
),
maven.artifact(
"org.junit.platform",
"junit-platform-launcher",
"1.10.1",
testonly = True,
),
maven.artifact(
"org.junit.platform",
"junit-platform-reporting",
"1.10.1",
testonly = True,
),
maven.artifact(
"com.google.code.gson",
"gson",
"2.10.1",
testonly = False,
),
maven.artifact(
"org.hamcrest",
"hamcrest-all",
"1.3",
testonly = True,
),
maven.artifact(
"com.googlecode.junit-toolbox",
"junit-toolbox",
"2.4",
testonly = True,
),
maven.artifact(
"org.apache.ant",
"ant",
"1.10.12",
testonly = True,
),
maven.artifact(
"org.apache.ant",
"ant-junit",
"1.10.12",
testonly = True,
),
maven.artifact(
"org.mockito",
"mockito-core",
"4.1.0",
testonly = True,
),
maven.artifact(
"com.google.testing.compile",
"compile-testing",
"0.21.0",
testonly = True,
),
]
maven_install(
name = "maven",
artifacts = maven_artifacts,
maven_install_json = "//:maven_install.json",
repositories = [
"https://repo1.maven.org/maven2",
"https://frcmaven.wpi.edu/artifactory/release/",
],
)
load("@maven//:defs.bzl", "pinned_maven_install")
pinned_maven_install()
# Setup aspect lib
http_archive(
name = "aspect_bazel_lib",
sha256 = "a8a92645e7298bbf538aa880131c6adb4cf6239bbd27230f077a00414d58e4ce",
strip_prefix = "bazel-lib-2.7.2",
url = "https://github.com/aspect-build/bazel-lib/releases/download/v2.7.2/bazel-lib-v2.7.2.tar.gz",
)
load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies")
aspect_bazel_lib_dependencies()
# Download toolchains
http_archive(
name = "rules_bzlmodrio_toolchains",
@@ -99,8 +213,8 @@ setup_legacy_bzlmodrio_ni_cpp_dependencies()
http_archive(
name = "bzlmodrio-opencv",
sha256 = "ba3f4910ce9cc0e08abff732aeb5835b1bcfd864ca5296edeadcf2935f7e81b9",
url = "https://github.com/wpilibsuite/bzlmodRio-opencv/releases/download/2025.4.10.0-3.bcr1/bzlmodRio-opencv-2025.4.10.0-3.bcr1.tar.gz",
sha256 = "6e8544fae07ed5b4fedc146f6ad083d0d8947e3efb5332a20abc46601a52a1b5",
url = "https://github.com/wpilibsuite/bzlmodRio-opencv/releases/download/2025.4.10.0-3.bcr2/bzlmodRio-opencv-2025.4.10.0-3.bcr2.tar.gz",
)
load("@bzlmodrio-opencv//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_opencv_cpp_dependencies")
@@ -111,6 +225,17 @@ load("@bzlmodrio-opencv//:maven_java_deps.bzl", "setup_legacy_bzlmodrio_opencv_j
setup_legacy_bzlmodrio_opencv_java_dependencies()
http_archive(
name = "bzlmodrio-libssh",
sha256 = "65caef82554617403a16c79e8bcac6553d40eca3e23197e63275bba22db7d5b5",
strip_prefix = "bzlmodRio-libssh-8405fbd5eb4e42b495f08f6ccf6fbbe5ced28bb7",
urls = ["https://github.com/wpilibsuite/bzlmodRio-libssh/archive/8405fbd5eb4e42b495f08f6ccf6fbbe5ced28bb7.tar.gz"],
)
load("@bzlmodrio-libssh//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_libssh_cpp_dependencies")
setup_legacy_bzlmodrio_libssh_cpp_dependencies()
http_archive(
name = "build_bazel_apple_support",
sha256 = "c4bb2b7367c484382300aee75be598b92f847896fb31bbd22f3a2346adf66a80",
@@ -123,3 +248,43 @@ load(
)
apple_support_dependencies()
# Setup quickbuf compiler
QUICKBUF_VERSION = "1.3.2"
http_file(
name = "quickbuffer_protoc_linux",
executable = True,
sha256 = "f9a041bccaa7040db523666ef1b5fe9f6f94e70a82c88951f18f58aadd9c50b5",
url = "https://repo1.maven.org/maven2/us/hebi/quickbuf/protoc-gen-quickbuf/" + QUICKBUF_VERSION + "/protoc-gen-quickbuf-" + QUICKBUF_VERSION + "-linux-x86_64.exe",
)
http_file(
name = "quickbuffer_protoc_osx",
executable = True,
sha256 = "ea307c2b69664ae7e7c69db4cddf5803187e5a34bceffd09a21652f0f16044f7",
url = "https://repo1.maven.org/maven2/us/hebi/quickbuf/protoc-gen-quickbuf/" + QUICKBUF_VERSION + "/protoc-gen-quickbuf-" + QUICKBUF_VERSION + "-osx-x86_64.exe ",
)
http_file(
name = "quickbuffer_protoc_windows",
executable = True,
sha256 = "27dc1f29764a62b5e6a813a4bcd63e81bbdc3394da760a44acae1025b4a89f1d",
url = "https://repo1.maven.org/maven2/us/hebi/quickbuf/protoc-gen-quickbuf/" + QUICKBUF_VERSION + "/protoc-gen-quickbuf-" + QUICKBUF_VERSION + "-windows-x86_64.exe ",
)
# Setup rules_proto
http_archive(
name = "rules_proto",
sha256 = "0e5c64a2599a6e26c6a03d6162242d231ecc0de219534c38cb4402171def21e8",
strip_prefix = "rules_proto-7.0.2",
url = "https://github.com/bazelbuild/rules_proto/releases/download/7.0.2/rules_proto-7.0.2.tar.gz",
)
load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies")
rules_proto_dependencies()
load("@rules_proto//proto:setup.bzl", "rules_proto_setup")
rules_proto_setup()

View File

@@ -1,6 +1,9 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
load("@rules_java//java:defs.bzl", "java_binary")
load("@rules_python//python:defs.bzl", "py_binary")
load("//shared/bazel/rules:cc_rules.bzl", "wpilib_cc_library")
load("//shared/bazel/rules:java_rules.bzl", "wpilib_java_junit5_test")
load("//shared/bazel/rules:jni_rules.bzl", "wpilib_jni_cc_library", "wpilib_jni_java_library")
load("//shared/bazel/rules/gen:gen-resources.bzl", "generate_resources")
cc_library(
@@ -31,7 +34,6 @@ cc_library(
}),
includes = ["src/main/native/thirdparty/apriltag/include/common"],
strip_include_prefix = "src/main/native/thirdparty/apriltag/include",
visibility = ["//visibility:public"],
)
generate_resources(
@@ -39,10 +41,9 @@ generate_resources(
namespace = "frc",
prefix = "APRILTAG",
resource_files = glob(["src/main/native/resources/**"]),
visibility = ["//visibility:public"],
)
cc_library(
wpilib_cc_library(
name = "apriltag.static",
srcs = [":generate-resources"] + glob(
["src/main/native/cpp/**"],
@@ -59,9 +60,20 @@ cc_library(
],
)
java_library(
wpilib_jni_cc_library(
name = "apriltagjni",
srcs = glob(["src/main/native/cpp/jni/**"]),
java_dep = ":apriltag-java",
visibility = ["//visibility:public"],
deps = [
":apriltag.static",
],
)
wpilib_jni_java_library(
name = "apriltag-java",
srcs = glob(["src/main/java/**/*.java"]),
native_libs = [":apriltagjni"],
resource_strip_prefix = "apriltag/src/main/native/resources",
resources = glob(["src/main/native/resources/**"]),
visibility = ["//visibility:public"],
@@ -88,6 +100,20 @@ cc_test(
],
)
wpilib_java_junit5_test(
name = "apriltag-java-test",
srcs = glob(["src/test/java/**/*.java"]),
resource_strip_prefix = "apriltag/src/test/resources",
resources = glob(["src/test/resources/**"]),
deps = [
":apriltag-java",
"//wpimath:wpimath-java",
"//wpiutil:wpiutil-java",
"@bzlmodrio-opencv//libraries/java/opencv",
"@maven//:com_fasterxml_jackson_core_jackson_databind",
],
)
cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
@@ -108,5 +134,8 @@ java_binary(
py_binary(
name = "convert_apriltag_layouts",
srcs = ["convert_apriltag_layouts.py"],
tags = ["manual"],
target_compatible_with = select({
"@rules_bzlmodrio_toolchains//constraints/is_systemcore:systemcore": ["@platforms//:incompatible"],
"//conditions:default": [],
}),
)

View File

@@ -103,14 +103,8 @@ model {
return
}
it.cppCompiler.define 'WPILIB_EXPORTS'
if (it.component.name == "${nativeName}JNI") {
lib project: ':wpimath', library: 'wpimath', linkage: 'static'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
} else {
lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
}
lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
}
}
tasks {

View File

@@ -1,92 +0,0 @@
# Testing steps for real hardware
trigger:
batch: true
branches:
include:
- master
stages:
- stage: Build
jobs:
- job: IntegrationTests
displayName: Integration Tests
pool:
vmImage: 'ubuntu-latest'
container:
image: wpilib/roborio-cross-ubuntu:2023-22.04
timeoutInMinutes: 0
steps:
- task: Gradle@2
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
inputs:
workingDirectory: ""
gradleWrapperFile: "gradlew"
gradleOptions: "-Xmx3072m"
publishJUnitResults: false
testResultsFiles: "**/TEST-*.xml"
tasks: "copyWpilibJIntegrationTestJarToOutput copyWpilibCTestLibrariesToOutput"
options: "-Ponlylinuxathena -PbuildServer -PskipJavaFormat"
- task: PublishPipelineArtifact@0
inputs:
artifactName: "Integration Tests"
targetPath: "build/integrationTestFiles"
- stage: TestBench
displayName: Test Bench
condition: false
jobs:
- job: Cpp
displayName: C++
pool: RoboRioConnections
timeoutInMinutes: 30
workspace:
clean: all
steps:
- task: DownloadPipelineArtifact@0
inputs:
artifactName: "Integration Tests"
targetPath: "build/integrationTestFiles"
- task: ShellScript@2
displayName: Run C++ Tests
inputs:
scriptPath: test-scripts/deploy-and-run-test-on-robot.sh
args: 'cpp -A "--gtest_output=xml:/home/admin/testResults/cppreport.xml"'
- task: PublishTestResults@2
displayName: Publish C++ Test Results
inputs:
testResultsFormat: "JUnit"
testResultsFiles: "*.xml"
testRunTitle: "C++ Test Report"
searchFolder: "$(System.DefaultWorkingDirectory)/test-reports"
- job: Java
pool: RoboRioConnections
timeoutInMinutes: 30
workspace:
clean: all
steps:
- task: DownloadPipelineArtifact@0
inputs:
artifactName: "Integration Tests"
targetPath: "build/integrationTestFiles"
- task: ShellScript@2
displayName: Run Java Tests
inputs:
scriptPath: test-scripts/deploy-and-run-test-on-robot.sh
args: "java"
- task: PublishTestResults@2
displayName: Publish Java Test Results
inputs:
testResultsFormat: "JUnit"
testResultsFiles: "*.xml"
testRunTitle: "Java Test Report"
searchFolder: "$(System.DefaultWorkingDirectory)/test-reports"

View File

@@ -11,7 +11,7 @@ buildscript {
plugins {
id 'base'
id 'edu.wpi.first.wpilib.versioning.WPILibVersioningPlugin' version '2023.0.1'
id 'edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin' version '2020.2'
id 'edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin' version '2025.0'
id 'edu.wpi.first.NativeUtils' apply false
id 'edu.wpi.first.GradleJni' version '1.1.0'
id 'edu.wpi.first.GradleVsCode'
@@ -32,6 +32,7 @@ allprojects {
url = 'https://frcmaven.wpi.edu/artifactory/ex-mvn'
}
}
wpilibRepositories.use2027Repos()
if (project.hasProperty('releaseMode')) {
wpilibRepositories.addAllReleaseRepositories(it)
} else {

View File

@@ -9,5 +9,5 @@ repositories {
}
}
dependencies {
implementation "edu.wpi.first:native-utils:2025.9.1"
implementation "edu.wpi.first:native-utils:2025.12.1"
}

View File

@@ -1,7 +1,8 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_test")
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
load("//shared/bazel/rules:cc_rules.bzl", "wpilib_cc_library")
cc_library(
wpilib_cc_library(
name = "cameraserver.static",
srcs = glob(["src/main/native/cpp/**"]),
hdrs = glob(["src/main/native/include/**/*"]),

View File

@@ -61,9 +61,6 @@ model {
lib project: ':cscore', library: 'cscore', linkage: 'static'
lib project: ':wpinet', library: 'wpinet', linkage: 'static'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
nativeUtils.useRequiredLibrary(binary, 'ni_link_libraries', 'ni_runtime_libraries')
}
}
}
}

View File

@@ -176,7 +176,7 @@ public final class Main {
} else {
System.out.println("Setting up NetworkTables client for team " + team);
ntinst.setServerTeam(team);
ntinst.startClient4("multicameraserver");
ntinst.startClient("multicameraserver");
}
// start cameras

View File

@@ -190,7 +190,7 @@ int main(int argc, char* argv[]) {
ntinst.StartServer();
} else {
wpi::print("Setting up NetworkTables client for team {}\n", team);
ntinst.StartClient4("multicameraserver");
ntinst.StartClient("multicameraserver");
ntinst.SetServerTeam(team);
}

View File

@@ -4,7 +4,6 @@
package edu.wpi.first.cameraserver;
import edu.wpi.first.cscore.AxisCamera;
import edu.wpi.first.cscore.CameraServerJNI;
import edu.wpi.first.cscore.CvSink;
import edu.wpi.first.cscore.CvSource;
@@ -541,9 +540,7 @@ public final class CameraServer {
* @return The USB camera capturing images.
*/
public static UsbCamera startAutomaticCapture() {
UsbCamera camera = startAutomaticCapture(m_defaultUsbDevice.getAndIncrement());
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
return camera;
return startAutomaticCapture(m_defaultUsbDevice.getAndIncrement());
}
/**
@@ -558,7 +555,7 @@ public final class CameraServer {
public static UsbCamera startAutomaticCapture(int dev) {
UsbCamera camera = new UsbCamera("USB Camera " + dev, dev);
startAutomaticCapture(camera);
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
CameraServerSharedStore.reportUsage("UsbCamera[" + dev + "]", "auto");
return camera;
}
@@ -572,7 +569,7 @@ public final class CameraServer {
public static UsbCamera startAutomaticCapture(String name, int dev) {
UsbCamera camera = new UsbCamera(name, dev);
startAutomaticCapture(camera);
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
CameraServerSharedStore.reportUsage("UsbCamera[" + dev + "]", "name");
return camera;
}
@@ -586,7 +583,7 @@ public final class CameraServer {
public static UsbCamera startAutomaticCapture(String name, String path) {
UsbCamera camera = new UsbCamera(name, path);
startAutomaticCapture(camera);
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
CameraServerSharedStore.reportUsage("UsbCamera[" + path + "]", "path");
return camera;
}
@@ -603,72 +600,6 @@ public final class CameraServer {
return server;
}
/**
* Adds an Axis IP camera.
*
* <p>This overload calls {@link #addAxisCamera(String, String)} with name "Axis Camera".
*
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
* @return The Axis camera capturing images.
* @deprecated Call startAutomaticCapture with a HttpCamera instead.
*/
@Deprecated(forRemoval = true, since = "2025")
@SuppressWarnings("removal")
public static AxisCamera addAxisCamera(String host) {
return addAxisCamera("Axis Camera", host);
}
/**
* Adds an Axis IP camera.
*
* <p>This overload calls {@link #addAxisCamera(String, String[])} with name "Axis Camera".
*
* @param hosts Array of Camera host IPs/DNS names
* @return The Axis camera capturing images.
* @deprecated Call startAutomaticCapture with a HttpCamera instead.
*/
@Deprecated(forRemoval = true, since = "2025")
@SuppressWarnings("removal")
public static AxisCamera addAxisCamera(String[] hosts) {
return addAxisCamera("Axis Camera", hosts);
}
/**
* Adds an Axis IP camera.
*
* @param name The name to give the camera
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
* @return The Axis camera capturing images.
* @deprecated Call startAutomaticCapture with a HttpCamera instead.
*/
@Deprecated(forRemoval = true, since = "2025")
@SuppressWarnings("removal")
public static AxisCamera addAxisCamera(String name, String host) {
AxisCamera camera = new AxisCamera(name, host);
// Create a passthrough MJPEG server for USB access
startAutomaticCapture(camera);
CameraServerSharedStore.getCameraServerShared().reportAxisCamera(camera.getHandle());
return camera;
}
/**
* Adds an Axis IP camera.
*
* @param name The name to give the camera
* @param hosts Array of Camera host IPs/DNS names
* @return The Axis camera capturing images.
* @deprecated Call startAutomaticCapture with a HttpCamera instead.
*/
@Deprecated(forRemoval = true, since = "2025")
@SuppressWarnings("removal")
public static AxisCamera addAxisCamera(String name, String[] hosts) {
AxisCamera camera = new AxisCamera(name, hosts);
// Create a passthrough MJPEG server for USB access
startAutomaticCapture(camera);
CameraServerSharedStore.getCameraServerShared().reportAxisCamera(camera.getHandle());
return camera;
}
/**
* Adds a virtual camera for switching between two streams. Unlike the other addCamera methods,
* this returns a VideoSink rather than a VideoSource. Calling setSource() on the returned object

View File

@@ -21,25 +21,12 @@ public interface CameraServerShared {
void reportDriverStationError(String error);
/**
* Report an video server usage.
* Report usage.
*
* @param id the usage id
* @param resource the resource name
* @param data arbitrary string data
*/
void reportVideoServer(int id);
/**
* Report a usb camera usage.
*
* @param id the usage id
*/
void reportUsbCamera(int id);
/**
* Report an axis camera usage.
*
* @param id the usage id
*/
void reportAxisCamera(int id);
void reportUsage(String resource, String data);
/**
* Get if running on a roboRIO.

View File

@@ -20,17 +20,11 @@ public final class CameraServerSharedStore {
cameraServerShared =
new CameraServerShared() {
@Override
public void reportVideoServer(int id) {}
@Override
public void reportUsbCamera(int id) {}
public void reportUsage(String resource, String data) {}
@Override
public void reportDriverStationError(String error) {}
@Override
public void reportAxisCamera(int id) {}
@Override
public Long getRobotMainThreadId() {
return null;
@@ -40,6 +34,16 @@ public final class CameraServerSharedStore {
return cameraServerShared;
}
/**
* Report usage.
*
* @param resource the resource name
* @param data arbitrary string data
*/
public static void reportUsage(String resource, String data) {
getCameraServerShared().reportUsage(resource, data);
}
/**
* Set the CameraServerShared object.
*

View File

@@ -184,9 +184,9 @@ std::vector<std::string> Instance::GetSourceStreamValues(CS_Source source) {
value = "mjpg:" + value;
}
#ifdef __FRC_ROBORIO__
#ifdef __FRC_SYSTEMCORE__
// Look to see if we have a passthrough server for this source
// Only do this on the roboRIO
// Only do this on the systemcore
for (const auto& i : m_sinks) {
CS_Sink sink = i.second.GetHandle();
CS_Source sinkSource = cs::GetSinkSource(sink, &status);
@@ -471,11 +471,7 @@ Instance::Instance() {
}
cs::UsbCamera CameraServer::StartAutomaticCapture() {
cs::UsbCamera camera =
StartAutomaticCapture(::GetInstance().m_defaultUsbDevice++);
auto csShared = GetCameraServerShared();
csShared->ReportUsbCamera(camera.GetHandle());
return camera;
return StartAutomaticCapture(::GetInstance().m_defaultUsbDevice++);
}
cs::UsbCamera CameraServer::StartAutomaticCapture(int dev) {
@@ -483,7 +479,7 @@ cs::UsbCamera CameraServer::StartAutomaticCapture(int dev) {
cs::UsbCamera camera{fmt::format("USB Camera {}", dev), dev};
StartAutomaticCapture(camera);
auto csShared = GetCameraServerShared();
csShared->ReportUsbCamera(camera.GetHandle());
csShared->ReportUsage(fmt::format("UsbCamera[{}]", dev), "auto");
return camera;
}
@@ -493,7 +489,7 @@ cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name,
cs::UsbCamera camera{name, dev};
StartAutomaticCapture(camera);
auto csShared = GetCameraServerShared();
csShared->ReportUsbCamera(camera.GetHandle());
csShared->ReportUsage(fmt::format("UsbCamera[{}]", dev), "name");
return camera;
}
@@ -503,67 +499,10 @@ cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name,
cs::UsbCamera camera{name, path};
StartAutomaticCapture(camera);
auto csShared = GetCameraServerShared();
csShared->ReportUsbCamera(camera.GetHandle());
csShared->ReportUsage(fmt::format("UsbCamera[{}]", path), "path");
return camera;
}
WPI_IGNORE_DEPRECATED
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view host) {
return AddAxisCamera("Axis Camera", host);
}
cs::AxisCamera CameraServer::AddAxisCamera(const char* host) {
return AddAxisCamera("Axis Camera", host);
}
cs::AxisCamera CameraServer::AddAxisCamera(const std::string& host) {
return AddAxisCamera("Axis Camera", host);
}
cs::AxisCamera CameraServer::AddAxisCamera(std::span<const std::string> hosts) {
return AddAxisCamera("Axis Camera", hosts);
}
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
std::string_view host) {
::GetInstance();
cs::AxisCamera camera{name, host};
StartAutomaticCapture(camera);
auto csShared = GetCameraServerShared();
csShared->ReportAxisCamera(camera.GetHandle());
return camera;
}
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
const char* host) {
::GetInstance();
cs::AxisCamera camera{name, host};
StartAutomaticCapture(camera);
auto csShared = GetCameraServerShared();
csShared->ReportAxisCamera(camera.GetHandle());
return camera;
}
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
const std::string& host) {
::GetInstance();
cs::AxisCamera camera{name, host};
StartAutomaticCapture(camera);
auto csShared = GetCameraServerShared();
csShared->ReportAxisCamera(camera.GetHandle());
return camera;
}
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
std::span<const std::string> hosts) {
::GetInstance();
cs::AxisCamera camera{name, hosts};
StartAutomaticCapture(camera);
auto csShared = GetCameraServerShared();
csShared->ReportAxisCamera(camera.GetHandle());
return camera;
}
WPI_UNIGNORE_DEPRECATED
cs::MjpegServer CameraServer::AddSwitchedCamera(std::string_view name) {
auto& inst = ::GetInstance();
// create a dummy CvSource

View File

@@ -12,9 +12,7 @@
namespace {
class DefaultCameraServerShared : public frc::CameraServerShared {
public:
void ReportUsbCamera(int id) override {}
void ReportAxisCamera(int id) override {}
void ReportVideoServer(int id) override {}
void ReportUsage(std::string_view resource, std::string_view data) override {}
void SetCameraServerErrorV(fmt::string_view format,
fmt::format_args args) override {}
void SetVisionRunnerErrorV(fmt::string_view format,

View File

@@ -11,8 +11,6 @@
#include <string_view>
#include <vector>
#include <wpi/deprecated.h>
#include "cscore_cv.h"
namespace frc {
@@ -75,128 +73,6 @@ class CameraServer {
*/
static cs::MjpegServer StartAutomaticCapture(const cs::VideoSource& camera);
WPI_IGNORE_DEPRECATED
/**
* Adds an Axis IP camera.
*
* This overload calls AddAxisCamera() with name "Axis Camera".
*
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
*/
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
static cs::AxisCamera AddAxisCamera(std::string_view host);
/**
* Adds an Axis IP camera.
*
* This overload calls AddAxisCamera() with name "Axis Camera".
*
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
*/
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
static cs::AxisCamera AddAxisCamera(const char* host);
/**
* Adds an Axis IP camera.
*
* This overload calls AddAxisCamera() with name "Axis Camera".
*
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
*/
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
static cs::AxisCamera AddAxisCamera(const std::string& host);
/**
* Adds an Axis IP camera.
*
* This overload calls AddAxisCamera() with name "Axis Camera".
*
* @param hosts Array of Camera host IPs/DNS names
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
*/
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
static cs::AxisCamera AddAxisCamera(std::span<const std::string> hosts);
/**
* Adds an Axis IP camera.
*
* This overload calls AddAxisCamera() with name "Axis Camera".
*
* @param hosts Array of Camera host IPs/DNS names
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
*/
template <typename T>
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
static cs::AxisCamera AddAxisCamera(std::initializer_list<T> hosts) {
return AddAxisCamera("Axis Camera", hosts);
}
/**
* Adds an Axis IP camera.
*
* @param name The name to give the camera
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
*/
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
static cs::AxisCamera AddAxisCamera(std::string_view name,
std::string_view host);
/**
* Adds an Axis IP camera.
*
* @param name The name to give the camera
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
*/
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
static cs::AxisCamera AddAxisCamera(std::string_view name, const char* host);
/**
* Adds an Axis IP camera.
*
* @param name The name to give the camera
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
*/
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
static cs::AxisCamera AddAxisCamera(std::string_view name,
const std::string& host);
/**
* Adds an Axis IP camera.
*
* @param name The name to give the camera
* @param hosts Array of Camera host IPs/DNS names
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
*/
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
static cs::AxisCamera AddAxisCamera(std::string_view name,
std::span<const std::string> hosts);
/**
* Adds an Axis IP camera.
*
* @param name The name to give the camera
* @param hosts Array of Camera host IPs/DNS names
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
*/
template <typename T>
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
static cs::AxisCamera 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
/**
* Adds a virtual camera for switching between two streams. Unlike the
* other addCamera methods, this returns a VideoSink rather than a

View File

@@ -5,6 +5,7 @@
#pragma once
#include <memory>
#include <string_view>
#include <thread>
#include <utility>
@@ -14,9 +15,8 @@ namespace frc {
class CameraServerShared {
public:
virtual ~CameraServerShared() = default;
virtual void ReportUsbCamera(int id) = 0;
virtual void ReportAxisCamera(int id) = 0;
virtual void ReportVideoServer(int id) = 0;
virtual void ReportUsage(std::string_view resource,
std::string_view data) = 0;
virtual void SetCameraServerErrorV(fmt::string_view format,
fmt::format_args args) = 0;
virtual void SetVisionRunnerErrorV(fmt::string_view format,

View File

@@ -46,7 +46,7 @@ macro(wpilib_target_warnings target)
# Suppress warning "enumeration types with a fixed underlying type are a
# Clang extension"
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT EMSCRIPTEN)
target_compile_options(${target} PRIVATE $<$<COMPILE_LANGUAGE:C>:-Wno-fixed-enum-extension>)
endif()

View File

@@ -1,115 +0,0 @@
import org.gradle.language.base.internal.ProjectLayout
import edu.wpi.first.deployutils.deploy.target.RemoteTarget
import edu.wpi.first.deployutils.deploy.target.location.SshDeployLocation
import edu.wpi.first.deployutils.deploy.artifact.*
import org.gradle.internal.os.OperatingSystem
apply plugin: 'cpp'
apply plugin: 'visual-studio'
apply plugin: 'edu.wpi.first.NativeUtils'
apply plugin: ExtraTasks
apply plugin: 'edu.wpi.first.DeployUtils'
apply from: '../shared/config.gradle'
ext {
sharedCvConfigs = [crossConnIntegrationTests: []]
staticCvConfigs = [:]
useJava = false
useCpp = true
staticGtestConfigs = [crossConnIntegrationTests: []]
}
apply from: "${rootDir}/shared/opencv.gradle"
apply from: "${rootDir}/shared/googletest.gradle"
deploy {
targets {
roborio(RemoteTarget) {
directory = '/home/admin'
maxChannels = 4
locations {
ssh(SshDeployLocation) {
address = "172.22.11.2"
user = 'admin'
password = ''
ipv6 = false
}
}
artifacts {
all {
predeploy << { ctx ->
ctx.execute('/usr/local/frc/bin/frcKillRobot.sh -t')
}
postdeploy << { ctx ->
ctx.execute("sync")
ctx.execute("ldconfig")
}
}
crossConnIntegrationTests(NativeExecutableArtifact) {
libraryDirectory = '/usr/local/frc/third-party/lib'
postdeploy << { ctx ->
ctx.execute('chmod +x crossConnIntegrationTests')
}
}
}
}
}
}
model {
components {
crossConnIntegrationTests(NativeExecutableSpec) {
targetBuildTypes 'debug'
binaries.all { binary ->
if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
if (binary.buildType.name == 'debug') {
deploy.targets.roborio.artifacts.crossConnIntegrationTests.binary = binary
}
binary.sources {
athenaCpp(CppSourceSet) {
source {
srcDirs = ['src/main/native/cpp']
includes = ['**/*.cpp']
}
exportedHeaders {
srcDirs = ['src/main/native/include']
includes = ['**/*.h']
}
}
}
project(':hal').addHalDependency(binary, 'shared')
project(':hal').addHalJniDependency(binary)
lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
lib project: ':thirdparty:googletest', library: 'googletest', linkage: 'static'
if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
nativeUtils.useRequiredLibrary(binary, 'ni_link_libraries', 'ni_runtime_libraries')
}
} else {
binary.sources {
simCpp(CppSourceSet) {
source {
srcDirs 'src/main/native/dt'
includes = ['**/*.cpp']
}
}
}
}
}
}
}
}
tasks.register('deployTests') {
try {
dependsOn tasks.named('deployCrossConnIntegrationTestsLibrariesRoborio')
dependsOn tasks.named('deployCrossConnIntegrationTestsRoborio')
} catch (ignored) {
}
}

View File

@@ -1,112 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <gtest/gtest.h>
#include <hal/AnalogInput.h>
#include <hal/AnalogOutput.h>
#include <wpi/SmallVector.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
using namespace hlt;
class AnalogCrossTest : public ::testing::TestWithParam<std::pair<int, int>> {};
TEST_P(AnalogCrossTest, AnalogCross) {
auto param = GetParam();
int32_t status = 0;
AnalogInputHandle input{param.first, &status};
ASSERT_EQ(0, status);
AnalogOutputHandle output{param.second, &status};
ASSERT_EQ(0, status);
for (double i = 0; i < 5; i += 0.1) {
HAL_SetAnalogOutput(output, i, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_NEAR(i, HAL_GetAnalogVoltage(input, &status), 0.01);
ASSERT_EQ(0, status);
}
for (double i = 5; i > 0; i -= 0.1) {
HAL_SetAnalogOutput(output, i, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_NEAR(i, HAL_GetAnalogVoltage(input, &status), 0.01);
ASSERT_EQ(0, status);
}
}
TEST(AnalogInputTest, AllocateAll) {
wpi::SmallVector<AnalogInputHandle, 21> analogHandles;
for (int i = 0; i < HAL_GetNumAnalogInputs(); i++) {
int32_t status = 0;
analogHandles.emplace_back(AnalogInputHandle(i, &status));
ASSERT_EQ(status, 0);
}
}
TEST(AnalogInputTest, MultipleAllocateFails) {
int32_t status = 0;
AnalogInputHandle handle(0, &status);
ASSERT_NE(handle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
AnalogInputHandle handle2(0, &status);
ASSERT_EQ(handle2, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
TEST(AnalogInputTest, OverAllocateFails) {
int32_t status = 0;
AnalogInputHandle handle(HAL_GetNumAnalogInputs(), &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(AnalogInputTest, UnderAllocateFails) {
int32_t status = 0;
AnalogInputHandle handle(-1, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(AnalogOutputTest, AllocateAll) {
wpi::SmallVector<AnalogOutputHandle, 21> analogHandles;
for (int i = 0; i < HAL_GetNumAnalogOutputs(); i++) {
int32_t status = 0;
analogHandles.emplace_back(AnalogOutputHandle(i, &status));
ASSERT_EQ(status, 0);
}
}
TEST(AnalogOutputTest, MultipleAllocateFails) {
int32_t status = 0;
AnalogOutputHandle handle(0, &status);
ASSERT_NE(handle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
AnalogOutputHandle handle2(0, &status);
ASSERT_EQ(handle2, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
TEST(AnalogOutputTest, OverAllocateFails) {
int32_t status = 0;
AnalogOutputHandle handle(HAL_GetNumAnalogOutputs(), &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(AnalogOutputTest, UnderAllocateFails) {
int32_t status = 0;
AnalogOutputHandle handle(-1, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
INSTANTIATE_TEST_SUITE_P(AnalogCrossConnectsTests, AnalogCrossTest,
::testing::ValuesIn(AnalogCrossConnects));

View File

@@ -1,101 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <gtest/gtest.h>
#include <hal/DIO.h>
#include <wpi/SmallVector.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
using namespace hlt;
class DIOTest : public ::testing::TestWithParam<std::pair<int, int>> {};
TEST_P(DIOTest, DIOCross) {
auto param = GetParam();
int32_t status = 0;
DIOHandle first{param.first, false, &status};
ASSERT_EQ(0, status);
DIOHandle second{param.second, true, &status};
ASSERT_EQ(0, status);
HAL_SetDIO(first, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetDIO(first, &status));
ASSERT_EQ(0, status);
ASSERT_FALSE(HAL_GetDIO(second, &status));
ASSERT_EQ(0, status);
HAL_SetDIO(first, true, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_TRUE(HAL_GetDIO(second, &status));
ASSERT_EQ(0, status);
HAL_SetDIODirection(first, true, &status);
ASSERT_EQ(0, status);
HAL_SetDIODirection(second, false, &status);
ASSERT_EQ(0, status);
HAL_SetDIO(second, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetDIO(first, &status));
ASSERT_EQ(0, status);
HAL_SetDIO(second, true, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_TRUE(HAL_GetDIO(first, &status));
ASSERT_EQ(0, status);
}
TEST(DIOTest, AllocateAll) {
wpi::SmallVector<DIOHandle, 32> dioHandles;
for (int i = 0; i < HAL_GetNumDigitalChannels(); i++) {
int32_t status = 0;
dioHandles.emplace_back(i, true, &status);
ASSERT_EQ(status, 0);
}
}
TEST(DIOTest, MultipleAllocateFails) {
int32_t status = 0;
DIOHandle handle(0, true, &status);
ASSERT_NE(handle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
DIOHandle handle2(0, true, &status);
ASSERT_EQ(handle2, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
TEST(DIOTest, OverAllocateFails) {
int32_t status = 0;
DIOHandle handle(HAL_GetNumDigitalChannels(), true, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(DIOTest, UnderAllocateFails) {
int32_t status = 0;
DIOHandle handle(-1, true, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(DIOTest, CrossAllocationFails) {
int32_t status = 0;
PWMHandle pwmHandle(10, &status);
ASSERT_NE(pwmHandle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
DIOHandle handle(10, true, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
INSTANTIATE_TEST_SUITE_P(DIOCrossConnectsTests, DIOTest,
::testing::ValuesIn(DIOCrossConnects));

View File

@@ -1,52 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <gtest/gtest.h>
#include <hal/HAL.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
using namespace hlt;
class DutyCycleTest : public ::testing::TestWithParam<std::pair<int, int>> {};
TEST_P(DutyCycleTest, DutyCycle) {
auto param = GetParam();
int32_t status = 0;
PWMHandle pwmHandle(param.first, &status);
ASSERT_NE(pwmHandle, HAL_kInvalidHandle);
ASSERT_EQ(0, status);
// Ensure our PWM is disabled, and set up properly
HAL_SetPWMPulseTimeMicroseconds(pwmHandle, 0, &status);
ASSERT_EQ(0, status);
HAL_SetPWMConfigMicroseconds(pwmHandle, 2000, 1000, 1000, 0, 0, &status);
HAL_SetPWMConfigMicroseconds(pwmHandle, 5050, 2525, 2525, 2525, 0, &status);
ASSERT_EQ(0, status);
HAL_SetPWMPeriodScale(pwmHandle, 0, &status);
ASSERT_EQ(0, status);
DIOHandle dioHandle{param.second, true, &status};
ASSERT_EQ(0, status);
DutyCycleHandle dutyCycle{dioHandle, &status};
ASSERT_EQ(0, status);
HAL_SetPWMSpeed(pwmHandle, 0.5, &status);
ASSERT_EQ(0, status);
// Sleep enough time for the frequency to converge
usleep(3500000);
ASSERT_NEAR(
1000 / 5.05,
static_cast<double>(HAL_GetDutyCycleFrequency(dutyCycle, &status)), 1);
// TODO measure output
}
INSTANTIATE_TEST_SUITE_P(DutyCycleCrossConnTests, DutyCycleTest,
::testing::ValuesIn(PWMCrossConnects));

View File

@@ -1,10 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <gtest/gtest.h>
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -1,359 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <atomic>
#include <thread>
#include <utility>
#include <gtest/gtest.h>
#include <hal/DMA.h>
#include <hal/HAL.h>
#include <wpi/SmallVector.h>
#include <wpi/condition_variable.h>
#include <wpi/priority_mutex.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
using namespace hlt;
class PWMTest : public ::testing::TestWithParam<std::pair<int, int>> {};
void TestTimingDMA(int squelch, std::pair<int, int> param) {
// Initialize DMA
int32_t status = 0;
DMAHandle dmaHandle(&status);
ASSERT_NE(dmaHandle, HAL_kInvalidHandle);
ASSERT_EQ(0, status);
status = 0;
PWMHandle pwmHandle(param.first, &status);
ASSERT_NE(pwmHandle, HAL_kInvalidHandle);
ASSERT_EQ(0, status);
// Ensure our PWM is disabled, and set up properly
HAL_SetPWMPulseTimeMicroseconds(pwmHandle, 0, &status);
HAL_SetPWMConfigMicroseconds(pwmHandle, 2000, 1000, 1000, 0, 0, &status);
HAL_SetPWMPeriodScale(pwmHandle, squelch, &status);
unsigned int checkPeriod = 0;
switch (squelch) {
case (0):
checkPeriod = 5050;
break;
case (1):
checkPeriod = 10100;
break;
case (3):
checkPeriod = 20200;
break;
}
status = 0;
DIOHandle dioHandle(param.second, true, &status);
ASSERT_NE(dioHandle, HAL_kInvalidHandle);
HAL_AddDMADigitalSource(dmaHandle, dioHandle, &status);
ASSERT_EQ(0, status);
HAL_SetDMAExternalTrigger(dmaHandle, dioHandle,
HAL_AnalogTriggerType::HAL_Trigger_kInWindow, true,
true, &status);
ASSERT_EQ(0, status);
// Loop to test 5 speeds
for (unsigned int testWidth = 1000; testWidth < 2100; testWidth += 250) {
HAL_StartDMA(dmaHandle, 1024, &status);
ASSERT_EQ(0, status);
while (true) {
int32_t remaining = 0;
HAL_DMASample testSample;
HAL_ReadDMA(dmaHandle, &testSample, 0.01, &remaining, &status);
if (remaining == 0) {
break;
}
}
HAL_SetPWMSpeed(pwmHandle, (testWidth - 1000) / 1000.0, &status);
constexpr const int kSampleCount = 15;
HAL_DMASample dmaSamples[kSampleCount];
int readCount = 0;
while (readCount < kSampleCount) {
status = 0;
int32_t remaining = 0;
HAL_DMAReadStatus readStatus = HAL_ReadDMA(
dmaHandle, &dmaSamples[readCount], 1.0, &remaining, &status);
ASSERT_EQ(0, status);
ASSERT_EQ(HAL_DMAReadStatus::HAL_DMA_OK, readStatus);
readCount++;
}
HAL_SetPWMSpeed(pwmHandle, 0, &status);
HAL_StopDMA(dmaHandle, &status);
// Find first rising edge
int startIndex = 4;
while (startIndex < 6) {
status = 0;
auto value = HAL_GetDMASampleDigitalSource(&dmaSamples[startIndex],
dioHandle, &status);
ASSERT_EQ(0, status);
if (value) {
break;
}
startIndex++;
}
ASSERT_LT(startIndex, 6);
// Check that samples alternate
bool previous = false;
int iterationCount = 0;
for (int i = startIndex; i < startIndex + 8; i++) {
auto value =
HAL_GetDMASampleDigitalSource(&dmaSamples[i], dioHandle, &status);
ASSERT_EQ(0, status);
ASSERT_NE(previous, value);
previous = !previous;
iterationCount++;
}
ASSERT_EQ(iterationCount, 8);
iterationCount = 0;
// Check width between samples
for (int i = startIndex; i < startIndex + 8; i += 2) {
auto width = HAL_GetDMASampleTime(&dmaSamples[i + 1], &status) -
HAL_GetDMASampleTime(&dmaSamples[i], &status);
ASSERT_NEAR(testWidth, width, 10);
iterationCount++;
}
ASSERT_EQ(iterationCount, 4);
iterationCount = 0;
// Check period between samples
for (int i = startIndex; i < startIndex + 6; i += 2) {
auto period = HAL_GetDMASampleTime(&dmaSamples[i + 2], &status) -
HAL_GetDMASampleTime(&dmaSamples[i], &status);
ASSERT_NEAR(checkPeriod, period, 10);
iterationCount++;
}
ASSERT_EQ(iterationCount, 3);
}
}
struct InterruptCheckData {
wpi::SmallVector<uint64_t, 8> risingStamps;
wpi::SmallVector<uint64_t, 8> fallingStamps;
wpi::priority_mutex mutex;
wpi::condition_variable cond;
HAL_InterruptHandle handle;
};
// TODO switch this to DMA
void TestTiming(int squelch, std::pair<int, int> param) {
// Initialize interrupt
int32_t status = 0;
InterruptHandle interruptHandle(&status);
// Ensure we have a valid interrupt handle
ASSERT_NE(interruptHandle, HAL_kInvalidHandle);
status = 0;
PWMHandle pwmHandle(param.first, &status);
ASSERT_NE(pwmHandle, HAL_kInvalidHandle);
// Ensure our PWM is disabled, and set up properly
HAL_SetPWMPulseTimeMicroseconds(pwmHandle, 0, &status);
HAL_SetPWMConfigMicroseconds(pwmHandle, 2000, 1000, 1000, 0, 0, &status);
HAL_SetPWMPeriodScale(pwmHandle, squelch, &status);
unsigned int checkPeriod = 0;
switch (squelch) {
case (0):
checkPeriod = 5050;
break;
case (1):
checkPeriod = 10100;
break;
case (3):
checkPeriod = 20200;
break;
}
status = 0;
DIOHandle dioHandle(param.second, true, &status);
ASSERT_NE(dioHandle, HAL_kInvalidHandle);
InterruptCheckData interruptData;
interruptData.handle = interruptHandle;
// Can use any type for the interrupt handle
HAL_RequestInterrupts(interruptHandle, dioHandle,
HAL_AnalogTriggerType::HAL_Trigger_kInWindow, &status);
HAL_SetInterruptUpSourceEdge(interruptHandle, true, true, &status);
// Loop to test 5 speeds
for (unsigned int i = 1000; i < 2100; i += 250) {
interruptData.risingStamps.clear();
interruptData.fallingStamps.clear();
std::atomic_bool runThread{true};
status = 0;
std::thread interruptThread([&]() {
while (runThread) {
int32_t threadStatus = 0;
auto mask =
HAL_WaitForInterrupt(interruptHandle, 5, true, &threadStatus);
if ((mask & 0x100) == 0x100 && interruptData.risingStamps.size() == 0 &&
interruptData.fallingStamps.size() == 0) {
// Falling edge at start of tracking. Skip
continue;
}
int32_t status = 0;
if ((mask & 0x1) == 0x1) {
auto ts = HAL_ReadInterruptRisingTimestamp(interruptHandle, &status);
// Rising Edge
interruptData.risingStamps.push_back(ts);
} else if ((mask & 0x100) == 0x100) {
auto ts = HAL_ReadInterruptFallingTimestamp(interruptHandle, &status);
// Falling Edge
interruptData.fallingStamps.push_back(ts);
}
if (interruptData.risingStamps.size() >= 4 &&
interruptData.fallingStamps.size() >= 4) {
interruptData.cond.notify_all();
runThread = false;
break;
}
}
});
// Ensure our interrupt actually got created correctly.
ASSERT_EQ(status, 0);
HAL_SetPWMSpeed(pwmHandle, (i - 1000) / 1000.0, &status);
ASSERT_EQ(status, 0);
{
std::unique_lock<wpi::priority_mutex> lock(interruptData.mutex);
// Wait for lock
// TODO: Add Timeout
auto timeout = interruptData.cond.wait_for(lock, std::chrono::seconds(2));
if (timeout == std::cv_status::timeout) {
runThread = false;
if (interruptThread.joinable()) {
interruptThread.join();
}
ASSERT_TRUE(false); // Exit test as failure on timeout
}
}
HAL_SetPWMPulseTimeMicroseconds(pwmHandle, 0, &status);
// Ensure our interrupts have the proper counts
ASSERT_EQ(interruptData.risingStamps.size(),
interruptData.fallingStamps.size());
ASSERT_GT(interruptData.risingStamps.size(), 0u);
ASSERT_EQ(interruptData.risingStamps.size() % 2, 0u);
ASSERT_EQ(interruptData.fallingStamps.size() % 2, 0u);
for (size_t j = 0; j < interruptData.risingStamps.size(); j++) {
uint64_t width =
interruptData.fallingStamps[j] - interruptData.risingStamps[j];
ASSERT_NEAR(width, i, 10);
}
for (unsigned int j = 0; j < interruptData.risingStamps.size() - 1; j++) {
uint64_t period =
interruptData.risingStamps[j + 1] - interruptData.risingStamps[j];
ASSERT_NEAR(period, checkPeriod, 10);
}
runThread = false;
if (interruptThread.joinable()) {
interruptThread.join();
}
}
}
TEST_P(PWMTest, Timing4x) {
auto param = GetParam();
TestTiming(3, param);
}
TEST_P(PWMTest, Timing2x) {
auto param = GetParam();
TestTiming(1, param);
}
TEST_P(PWMTest, Timing1x) {
auto param = GetParam();
TestTiming(0, param);
}
TEST_P(PWMTest, TimingDMA4x) {
auto param = GetParam();
TestTimingDMA(3, param);
}
TEST_P(PWMTest, TimingDMA2x) {
auto param = GetParam();
TestTimingDMA(1, param);
}
TEST_P(PWMTest, TimingDMA1x) {
auto param = GetParam();
TestTimingDMA(0, param);
}
TEST(PWMTest, AllocateAll) {
wpi::SmallVector<PWMHandle, 21> pwmHandles;
for (int i = 0; i < HAL_GetNumPWMChannels(); i++) {
int32_t status = 0;
pwmHandles.emplace_back(PWMHandle(i, &status));
ASSERT_EQ(status, 0);
}
}
TEST(PWMTest, MultipleAllocateFails) {
int32_t status = 0;
PWMHandle handle(0, &status);
ASSERT_NE(handle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
PWMHandle handle2(0, &status);
ASSERT_EQ(handle2, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
TEST(PWMTest, OverAllocateFails) {
int32_t status = 0;
PWMHandle handle(HAL_GetNumPWMChannels(), &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(PWMTest, UnderAllocateFails) {
int32_t status = 0;
PWMHandle handle(-1, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(PWMTest, CrossAllocationFails) {
int32_t status = 0;
DIOHandle dioHandle(10, true, &status);
ASSERT_NE(dioHandle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
PWMHandle handle(10, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
INSTANTIATE_TEST_SUITE_P(PWMCrossConnectTests, PWMTest,
::testing::ValuesIn(PWMCrossConnects));

View File

@@ -1,50 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <gtest/gtest.h>
#include <hal/AnalogInput.h>
#include <hal/Relay.h>
#include <wpi/SmallVector.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
using namespace hlt;
class RelayAnalogTest : public ::testing::TestWithParam<std::pair<int, int>> {};
TEST_P(RelayAnalogTest, RelayAnalogCross) {
auto param = GetParam();
int32_t status = 0;
RelayHandle relay{param.first, true, &status};
ASSERT_EQ(0, status);
AnalogInputHandle analog{param.second, &status};
ASSERT_EQ(0, status);
AnalogTriggerHandle trigger{analog, &status};
ASSERT_EQ(0, status);
HAL_SetAnalogTriggerLimitsVoltage(trigger, 1.5, 3.0, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(relay, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetAnalogTriggerTriggerState(trigger, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(relay, true, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_TRUE(HAL_GetAnalogTriggerTriggerState(trigger, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(relay, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetAnalogTriggerTriggerState(trigger, &status));
ASSERT_EQ(0, status);
}
INSTANTIATE_TEST_SUITE_P(RelayAnalogCrossConnectsTests, RelayAnalogTest,
::testing::ValuesIn(RelayAnalogCrossConnects));

View File

@@ -1,104 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <gtest/gtest.h>
#include <hal/Relay.h>
#include <wpi/SmallVector.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
using namespace hlt;
class RelayDigitalTest : public ::testing::TestWithParam<RelayCross> {};
TEST_P(RelayDigitalTest, RelayCross) {
auto param = GetParam();
int32_t status = 0;
RelayHandle fwd{param.Relay, true, &status};
ASSERT_EQ(0, status);
RelayHandle rev{param.Relay, false, &status};
ASSERT_EQ(0, status);
DIOHandle fwdInput{param.FwdDio, true, &status};
ASSERT_EQ(0, status);
DIOHandle revInput{param.RevDio, true, &status};
ASSERT_EQ(0, status);
HAL_SetRelay(fwd, false, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(rev, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetDIO(fwdInput, &status));
ASSERT_EQ(0, status);
ASSERT_FALSE(HAL_GetDIO(revInput, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(fwd, false, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(rev, true, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetDIO(fwdInput, &status));
ASSERT_EQ(0, status);
ASSERT_TRUE(HAL_GetDIO(revInput, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(fwd, true, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(rev, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_TRUE(HAL_GetDIO(fwdInput, &status));
ASSERT_EQ(0, status);
ASSERT_FALSE(HAL_GetDIO(revInput, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(fwd, true, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(rev, true, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_TRUE(HAL_GetDIO(fwdInput, &status));
ASSERT_EQ(0, status);
ASSERT_TRUE(HAL_GetDIO(revInput, &status));
ASSERT_EQ(0, status);
}
TEST(RelayDigitalTest, AllocateAll) {
wpi::SmallVector<RelayHandle, 32> relayHandles;
for (int i = 0; i < HAL_GetNumRelayChannels(); i++) {
int32_t status = 0;
relayHandles.emplace_back(i / 2, i % 2, &status);
ASSERT_EQ(status, 0);
}
}
TEST(RelayDigitalTest, MultipleAllocateFails) {
int32_t status = 0;
RelayHandle handle(0, true, &status);
ASSERT_NE(handle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
RelayHandle handle2(0, true, &status);
ASSERT_EQ(handle2, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
TEST(RelayDigitalTest, OverAllocateFails) {
int32_t status = 0;
RelayHandle handle(HAL_GetNumRelayChannels(), true, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(RelayDigitalTest, UnderAllocateFails) {
int32_t status = 0;
RelayHandle handle(-1, true, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
INSTANTIATE_TEST_SUITE_P(RelayDigitalCrossConnectsTests, RelayDigitalTest,
::testing::ValuesIn(RelayCrossConnects));

View File

@@ -1,74 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <cstdlib>
#include <cstring>
#include <thread>
#include <gtest/gtest.h>
#include <hal/HAL.h>
#include <wpi/print.h>
#include "mockds/MockDS.h"
using namespace std::chrono_literals;
class TestEnvironment : public testing::Environment {
bool m_alreadySetUp = false;
MockDS m_mockDS;
public:
TestEnvironment() {
// Only set up once. This allows gtest_repeat to be used to automatically
// repeat tests.
if (m_alreadySetUp) {
return;
}
m_alreadySetUp = true;
if (!HAL_Initialize(500, 0)) {
wpi::print(stderr, "FATAL ERROR: HAL could not be initialized\n");
std::exit(-1);
}
m_mockDS.Start();
// This sets up the network communications library to enable the driver
// station. After starting network coms, it will loop until the driver
// station returns that the robot is enabled, to ensure that tests will be
// able to run on the hardware.
HAL_ObserveUserProgramStarting();
wpi::print("Started coms\n");
int enableCounter = 0;
auto checkEnabled = []() {
HAL_ControlWord controlWord;
std::memset(&controlWord, 0, sizeof(controlWord));
HAL_GetControlWord(&controlWord);
return controlWord.enabled && controlWord.dsAttached;
};
HAL_RefreshDSData();
while (!checkEnabled()) {
if (enableCounter > 50) {
// Robot did not enable properly after 5 seconds.
// Force exit
wpi::print(stderr, " Failed to enable. Aborting\n");
std::terminate();
}
std::this_thread::sleep_for(100ms);
wpi::print("Waiting for enable: {}\n", enableCounter++);
HAL_RefreshDSData();
}
std::this_thread::sleep_for(500ms);
}
~TestEnvironment() override { m_mockDS.Stop(); }
};
testing::Environment* const environment =
testing::AddGlobalTestEnvironment(new TestEnvironment);

View File

@@ -1,86 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "MockDS.h"
#include <stdint.h>
#include <string_view>
#include <hal/cpp/fpga_clock.h>
#include <wpi/Logger.h>
#include <wpi/SmallVector.h>
#include <wpi/print.h>
#include <wpinet/UDPClient.h>
static void LoggerFunc(unsigned int level, const char* file, unsigned int line,
const char* msg) {
if (level == 20) {
wpi::print(stderr, "DS: {}\n", msg);
return;
}
std::string_view levelmsg;
if (level >= 50) {
levelmsg = "CRITICAL";
} else if (level >= 40) {
levelmsg = "ERROR";
} else if (level >= 30) {
levelmsg = "WARNING";
} else {
return;
}
wpi::print(stderr, "DS: {}: {} ({}:{})\n", levelmsg, msg, file, line);
}
static void generateEnabledDsPacket(wpi::SmallVectorImpl<uint8_t>& data,
uint16_t sendCount) {
data.clear();
data.push_back(sendCount >> 8);
data.push_back(sendCount);
data.push_back(0x01); // general data tag
data.push_back(0x04); // teleop enabled
data.push_back(0x10); // normal data request
data.push_back(0x00); // red 1 station
}
void MockDS::Start() {
if (m_active) {
return;
}
m_active = true;
m_thread = std::thread([&]() {
wpi::Logger logger(LoggerFunc);
wpi::UDPClient client(logger);
client.start();
auto timeout_time = hal::fpga_clock::now();
int initCount = 0;
uint16_t sendCount = 0;
wpi::SmallVector<uint8_t, 8> data;
while (m_active) {
// Keep 20ms intervals, and increase time to next interval
auto current = hal::fpga_clock::now();
while (timeout_time <= current) {
timeout_time += std::chrono::milliseconds(20);
}
std::this_thread::sleep_until(timeout_time);
generateEnabledDsPacket(data, sendCount++);
// ~10 disabled packets are required to make the robot actually enable
// 1 is definitely not enough.
if (initCount < 10) {
initCount++;
data[3] = 0;
}
client.send(data, "127.0.0.1", 1110);
}
client.shutdown();
});
}
void MockDS::Stop() {
m_active = false;
if (m_thread.joinable()) {
m_thread.join();
}
}

View File

@@ -1,23 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <atomic>
#include <thread>
class MockDS {
public:
MockDS() = default;
~MockDS() { Stop(); }
MockDS(const MockDS& other) = delete;
MockDS& operator=(const MockDS& other) = delete;
void Start();
void Stop();
private:
std::thread m_thread;
std::atomic_bool m_active{false};
};

View File

@@ -1,62 +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 <array>
#include <utility>
namespace hlt {
constexpr static std::array<std::pair<int, int>, 22> DIOCrossConnects{
std::pair{20, 25},
std::pair{19, 24},
std::pair{17, 13},
std::pair{16, 12},
std::pair{15, 11},
std::pair{14, 10},
std::pair{26, 2},
std::pair{27, 1},
std::pair{28, 0},
std::pair{29, 3},
std::pair{30, 4},
// Opposite direction
std::pair{25, 20},
std::pair{24, 19},
std::pair{13, 17},
std::pair{12, 16},
std::pair{11, 15},
std::pair{10, 14},
std::pair{2, 26},
std::pair{1, 27},
std::pair{0, 28},
std::pair{3, 29},
std::pair{4, 30},
};
// PWM on left, DIO on right
constexpr static std::array<std::pair<int, int>, 2> PWMCrossConnects{
std::pair{0, 18},
std::pair{16, 25},
};
// FWD only, relay on left
constexpr static std::array<std::pair<int, int>, 2> RelayAnalogCrossConnects{
std::pair{2, 0}, std::pair{3, 1}};
struct RelayCross {
int Relay;
int FwdDio;
int RevDio;
};
constexpr static std::array<RelayCross, 1> RelayCrossConnects{
RelayCross{0, 23, 22}};
// input on left
constexpr static std::array<std::pair<int, int>, 2> AnalogCrossConnects{
std::pair{2, 0}, std::pair{4, 1}};
} // namespace hlt

View File

@@ -1,86 +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 <hal/DMA.h>
#include <hal/DutyCycle.h>
#include <hal/HAL.h>
namespace hlt {
struct InterruptHandle : hal::Handle<HAL_InterruptHandle, HAL_CleanInterrupts> {
public:
explicit InterruptHandle(int32_t* status)
: Handle{HAL_InitializeInterrupts(status)} {}
};
struct DMAHandle : hal::Handle<HAL_DMAHandle, HAL_FreeDMA> {
public:
explicit DMAHandle(int32_t* status) : Handle{HAL_InitializeDMA(status)} {}
};
struct AnalogInputHandle
: hal::Handle<HAL_AnalogInputHandle, HAL_FreeAnalogInputPort> {
public:
AnalogInputHandle(int32_t port, int32_t* status)
: Handle{HAL_InitializeAnalogInputPort(HAL_GetPort(port), nullptr,
status)} {}
};
struct AnalogOutputHandle
: hal::Handle<HAL_AnalogOutputHandle, HAL_FreeAnalogOutputPort> {
public:
AnalogOutputHandle(int32_t port, int32_t* status)
: Handle{HAL_InitializeAnalogOutputPort(HAL_GetPort(port), nullptr,
status)} {}
};
struct AnalogTriggerHandle
: hal::Handle<HAL_AnalogTriggerHandle, HAL_CleanAnalogTrigger> {
public:
explicit AnalogTriggerHandle(HAL_AnalogInputHandle port, int32_t* status)
: Handle{HAL_InitializeAnalogTrigger(port, status)} {}
};
struct DutyCycleHandle : hal::Handle<HAL_DutyCycleHandle, HAL_FreeDutyCycle> {
public:
DutyCycleHandle(HAL_DigitalHandle port, int32_t* status)
: Handle{HAL_InitializeDutyCycle(
port, HAL_AnalogTriggerType::HAL_Trigger_kInWindow, status)} {}
};
struct DIOHandle : hal::Handle<HAL_DigitalHandle, HAL_FreeDIOPort> {
public:
DIOHandle() {}
DIOHandle(int32_t port, HAL_Bool input, int32_t* status)
: Handle{
HAL_InitializeDIOPort(HAL_GetPort(port), input, nullptr, status)} {}
};
struct PWMHandle : hal::Handle<HAL_DigitalHandle, HAL_FreePWMPort> {
public:
PWMHandle() {}
PWMHandle(int32_t port, int32_t* status)
: Handle{HAL_InitializePWMPort(HAL_GetPort(port), nullptr, status)} {}
};
struct RelayHandle : hal::Handle<HAL_RelayHandle, HAL_FreeRelayPort> {
public:
RelayHandle(int32_t port, HAL_Bool fwd, int32_t* status)
: Handle{
HAL_InitializeRelayPort(HAL_GetPort(port), fwd, nullptr, status)} {}
};
#define ASSERT_LAST_ERROR_STATUS(status, x) \
do { \
ASSERT_EQ(status, HAL_USE_LAST_ERROR); \
[[maybe_unused]] \
const char* lastErrorMessageInMacro = HAL_GetLastError(&status); \
ASSERT_EQ(status, x); \
} while (0)
} // namespace hlt

View File

@@ -38,8 +38,10 @@ includeOtherLibs {
^fmt/
^gtest/
^opencv2/
^imgui
^support/
^tcpsockets/
^wpi/
^wpigui
^wpinet/
}

View File

@@ -1,5 +1,9 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test", "objc_library")
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_test")
load("@rules_java//java:defs.bzl", "java_binary")
load("//shared/bazel/rules:cc_rules.bzl", "wpilib_cc_library")
load("//shared/bazel/rules:java_rules.bzl", "wpilib_java_junit5_test")
load("//shared/bazel/rules:jni_rules.bzl", "wpilib_jni_cc_library", "wpilib_jni_java_library")
load("//shared/bazel/rules:objectivec_rules.bzl", "wpilib_objc_library")
WIN_SRCS = glob([
"src/main/native/windows/**/*.cpp",
@@ -22,7 +26,7 @@ filegroup(
}),
)
objc_library(
wpilib_objc_library(
name = "cscore-mac",
srcs = glob([
"src/main/native/objcpp/**/*.mm",
@@ -32,15 +36,20 @@ objc_library(
"src/main/native/include/**/*",
"src/main/native/objcpp/**/*.h",
]),
copts = [
"-std=c++20",
],
include_arc = False,
includes = [
"src/main/native/cpp",
"src/main/native/include",
"src/main/native/objcpp",
],
tags = ["manual"],
sdk_frameworks = [
"CoreFoundation",
"AVFoundation",
"Foundation",
"CoreMedia",
"CoreVideo",
"IOKit",
],
deps = [
"//wpinet:wpinet.static",
"//wpiutil:wpiutil.static",
@@ -48,7 +57,7 @@ objc_library(
],
)
cc_library(
wpilib_cc_library(
name = "cscore.static",
srcs = [":native-srcs"] + glob(
["src/main/native/cpp/**"],
@@ -71,9 +80,20 @@ cc_library(
}),
)
java_library(
wpilib_jni_cc_library(
name = "cscorejni",
srcs = glob(["src/main/native/cpp/jni/**"]),
java_dep = ":cscore-java",
visibility = ["//visibility:public"],
deps = [
":cscore.static",
],
)
wpilib_jni_java_library(
name = "cscore-java",
srcs = glob(["src/main/java/**/*.java"]),
native_libs = [":cscorejni"],
visibility = ["//visibility:public"],
deps = [
"//wpiutil:wpiutil-java",
@@ -91,6 +111,15 @@ cc_test(
],
)
wpilib_java_junit5_test(
name = "cscore-java-test",
srcs = glob(["src/test/java/**/*.java"]),
deps = [
":cscore-java",
"//wpiutil:wpiutil-java",
],
)
cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
@@ -108,3 +137,24 @@ java_binary(
"//wpiutil:wpiutil-java",
],
)
[wpilib_cc_library(
name = example + "-examples",
srcs = glob([
"examples/" + example + "/*.cpp",
]),
tags = [
"wpi-example",
],
deps = [
"//cscore:cscore.static",
"//wpigui",
],
) for example in [
"enum_usb",
"httpcvstream",
"settings",
"usbcvstream",
"usbstream",
"usbviewer",
]]

View File

@@ -21,7 +21,7 @@ if(APPLE)
cscore
PROPERTIES
LINK_FLAGS
"-framework CoreFoundation -framework AVFoundation -framework Foundation -framework CoreMedia -framework CoreVideo"
"-framework CoreFoundation -framework AVFoundation -framework Foundation -framework CoreMedia -framework CoreVideo -framework IOKit"
)
elseif(MSVC)
target_sources(cscore PRIVATE ${cscore_windows_src})

View File

@@ -22,7 +22,7 @@ model {
enableCheckTask true
javaCompileTasks << compileJava
jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.roborio)
jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.systemcore)
jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.linuxarm32)
jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.linuxarm64)
@@ -60,13 +60,8 @@ model {
if (!it.buildable || !(it instanceof NativeBinarySpec)) {
return
}
if (it.component.name == "${nativeName}JNI") {
lib project: ':wpinet', library: 'wpinet', linkage: 'static'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
} else {
lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
}
lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
}
}
}
@@ -177,7 +172,7 @@ model {
components {
examplesMap.each { key, value ->
if (key == "usbviewer") {
if (!project.hasProperty('onlylinuxathena')) {
if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxsystemcore')) {
"${key}"(NativeExecutableSpec) {
targetBuildTypes 'debug'
binaries.all {
@@ -186,7 +181,7 @@ model {
lib project: ':wpigui', library: 'wpigui', linkage: 'static'
lib library: 'cscore', linkage: 'shared'
lib project: ':thirdparty:imgui_suite', library: 'imguiSuite', linkage: 'static'
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
if (it.targetPlatform.name == nativeUtils.wpi.platforms.systemcore) {
it.buildable = false
return
}
@@ -200,9 +195,6 @@ model {
it.linker.args << '-lGL'
}
}
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries')
}
}
sources.cpp.source {
srcDirs 'examples/' + "${key}"
@@ -217,9 +209,6 @@ model {
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
lib library: 'cscore', linkage: 'shared'
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries')
}
}
sources.cpp.source {
srcDirs 'examples/' + "${key}"

View File

@@ -3,14 +3,17 @@
// the WPILib BSD license file in the root directory of this project.
#include <atomic>
#include <memory>
#include <thread>
#include <utility>
#include <vector>
#define IMGUI_DEFINE_MATH_OPERATORS
#include <imgui.h>
#include <imgui_internal.h>
#include <opencv2/core/core.hpp>
#include <opencv2/core/mat.hpp>
#include <opencv2/imgproc.hpp>
#include <wpi/mutex.h>
#include <wpi/print.h>
#include <wpi/spinlock.h>
#include <wpigui.h>
@@ -21,10 +24,10 @@
namespace gui = wpi::gui;
int main() {
std::atomic<cv::Mat*> latestFrame{nullptr};
std::vector<cv::Mat*> sharedFreeList;
wpi::spinlock sharedFreeListMutex;
std::vector<cv::Mat*> sourceFreeList;
wpi::spinlock latestFrameMutex;
std::unique_ptr<cv::Mat> latestFrame;
wpi::mutex freeListMutex;
std::vector<std::unique_ptr<cv::Mat>> freeList;
std::atomic<bool> stopCamera{false};
cs::UsbCamera camera{"usbcam", 0};
@@ -42,36 +45,31 @@ int main() {
continue;
}
// get or create a mat, prefer sourceFreeList over sharedFreeList
cv::Mat* out;
if (!sourceFreeList.empty()) {
out = sourceFreeList.back();
sourceFreeList.pop_back();
} else {
{
std::scoped_lock lock(sharedFreeListMutex);
for (auto mat : sharedFreeList) {
sourceFreeList.emplace_back(mat);
}
sharedFreeList.clear();
}
if (!sourceFreeList.empty()) {
out = sourceFreeList.back();
sourceFreeList.pop_back();
// get or create a mat
std::unique_ptr<cv::Mat> out;
{
std::scoped_lock lock{freeListMutex};
if (!freeList.empty()) {
out = std::move(freeList.back());
freeList.pop_back();
} else {
out = new cv::Mat;
out = std::make_unique<cv::Mat>();
}
}
// convert to RGBA
cv::cvtColor(frame, *out, cv::COLOR_BGR2RGBA);
// make available
auto prev = latestFrame.exchange(out);
{
// make available
std::scoped_lock lock{latestFrameMutex};
latestFrame.swap(out);
}
// put prev on free list
if (prev) {
sourceFreeList.emplace_back(prev);
// put the previous frame on free list
if (out) {
std::scoped_lock lock{freeListMutex};
freeList.emplace_back(std::move(out));
}
}
});
@@ -80,7 +78,11 @@ int main() {
gui::Initialize("Hello World", 1024, 768);
gui::Texture tex;
gui::AddEarlyExecute([&] {
auto frame = latestFrame.exchange(nullptr);
std::unique_ptr<cv::Mat> frame;
{
std::scoped_lock lock{latestFrameMutex};
latestFrame.swap(frame);
}
if (frame) {
// create or update texture
if (!tex || frame->cols != tex.GetWidth() ||
@@ -90,9 +92,10 @@ int main() {
} else {
tex.Update(frame->data);
}
// put back on shared freelist
std::scoped_lock lock(sharedFreeListMutex);
sharedFreeList.emplace_back(frame);
{
std::scoped_lock lock{freeListMutex};
freeList.emplace_back(std::move(frame));
}
}
ImGui::SetNextWindowSize(ImVec2(640, 480), ImGuiCond_FirstUseEver);

View File

@@ -1,85 +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.
package edu.wpi.cscore;
import edu.wpi.cscore.VideoMode.PixelFormat;
import edu.wpi.cscore.raw.RawFrame;
import java.nio.ByteBuffer;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
public class RawCVMatSink extends ImageSink {
RawFrame frame = new RawFrame();
Mat tmpMat;
ByteBuffer origByteBuffer;
int width;
int height;
int pixelFormat;
int bgrValue = PixelFormat.kBGR.getValue();
private int getCVFormat(PixelFormat pixelFormat) {
return switch (pixelFormat) {
case kYUYV, kRGB565, kY16, kUYVY -> CvType.CV_8UC2;
case kBGR -> CvType.CV_8UC3;
case kBGRA -> CvType.CV_8UC4;
case kGray, kMJPEG, kUnknown -> CvType.CV_8UC1;
};
}
/**
* Create a sink for accepting OpenCV images. WaitForFrame() must be called on the created sink to
* get each new image.
*
* @param name Source name (arbitrary unique identifier)
*/
public RawCVMatSink(String name) {
super(CameraServerJNI.createRawSink(name));
}
/**
* Wait for the next frame and get the image. Times out (returning 0) after 0.225 seconds. The
* provided image will have three 3-bit channels stored in BGR order.
*
* @return Frame time, or 0 on error (call GetError() to obtain the error message)
*/
public long grabFrame(Mat image) {
return grabFrame(image, 0.225);
}
/**
* Wait for the next frame and get the image. Times out (returning 0) after timeout seconds. The
* provided image will have three 3-bit channels stored in BGR order.
*
* @return Frame time, or 0 on error (call GetError() to obtain the error message); the frame time
* is in 1 us increments.
*/
public long grabFrame(Mat image, double timeout) {
frame.setWidth(0);
frame.setHeight(0);
frame.setPixelFormat(bgrValue);
long rv = CameraServerJNI.grabSinkFrameTimeout(m_handle, frame, timeout);
if (rv <= 0) {
return rv;
}
if (frame.getDataByteBuffer() != origByteBuffer
|| width != frame.getWidth()
|| height != frame.getHeight()
|| pixelFormat != frame.getPixelFormat()) {
origByteBuffer = frame.getDataByteBuffer();
height = frame.getHeight();
width = frame.getWidth();
pixelFormat = frame.getPixelFormat();
tmpMat =
new Mat(
frame.getHeight(),
frame.getWidth(),
getCVFormat(VideoMode.getPixelFormatFromInt(pixelFormat)),
origByteBuffer);
}
tmpMat.copyTo(image);
return rv;
}
}

View File

@@ -1,60 +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.
package edu.wpi.cscore;
import edu.wpi.cscore.VideoMode.PixelFormat;
import org.opencv.core.Mat;
public class RawCVMatSource extends ImageSource {
/**
* Create an OpenCV source.
*
* @param name Source name (arbitrary unique identifier)
* @param mode Video mode being generated
*/
public RawCVMatSource(String name, VideoMode mode) {
super(
CameraServerJNI.createRawSource(
name, mode.pixelFormat.getValue(), mode.width, mode.height, mode.fps));
}
/**
* Create an OpenCV source.
*
* @param name Source name (arbitrary unique identifier)
* @param pixelFormat Pixel format
* @param width width
* @param height height
* @param fps fps
*/
public RawCVMatSource(
String name, VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
super(CameraServerJNI.createRawSource(name, pixelFormat.getValue(), width, height, fps));
}
/**
* Put an OpenCV image and notify sinks.
*
* <p>Only 8-bit single-channel or 3-channel (with BGR channel order) images are supported. If the
* format, depth or channel order is different, use Mat.convertTo() and/or cvtColor() to convert
* it first.
*
* @param image OpenCV image
*/
public void putFrame(Mat image) {
int channels = image.channels();
if (channels != 1 && channels != 3) {
throw new VideoException("Unsupported Image Type");
}
int imgType = channels == 1 ? PixelFormat.kGray.getValue() : PixelFormat.kBGR.getValue();
CameraServerJNI.putRawSourceFrame(
m_handle,
image.dataAddr(),
image.width(),
image.height(),
imgType,
(int) image.total() * channels);
}
}

View File

@@ -1025,10 +1025,10 @@ public class CameraServerJNI {
/**
* Runs main run loop with timeout.
*
* @param timeoutSeconds Timeout in seconds.
* @param timeout Timeout in seconds.
* @return 3 on timeout, 2 on signal, 1 on other.
*/
public static native int runMainRunLoopTimeout(double timeoutSeconds);
public static native int runMainRunLoopTimeout(double timeout);
/** Stops main run loop. */
public static native void stopMainRunLoop();

View File

@@ -2085,9 +2085,9 @@ Java_edu_wpi_first_cscore_CameraServerJNI_runMainRunLoop
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_cscore_CameraServerJNI_runMainRunLoopTimeout
(JNIEnv*, jclass, jdouble timeoutSeconds)
(JNIEnv*, jclass, jdouble timeout)
{
return cs::RunMainRunLoopTimeout(timeoutSeconds);
return cs::RunMainRunLoopTimeout(timeout);
}
/*

View File

@@ -5,7 +5,16 @@
#pragma once
namespace cs {
void RunMainRunLoop();
int RunMainRunLoopTimeout(double timeoutSeconds);
/**
* Runs main run loop with timeout.
*
* @param timeout Timeout in seconds.
*/
int RunMainRunLoopTimeout(double timeout);
void StopMainRunLoop();
} // namespace cs

View File

@@ -12,16 +12,16 @@ static wpi::Event& GetInstance() {
}
namespace cs {
void RunMainRunLoop() {
wpi::Event& event = GetInstance();
wpi::WaitForObject(event.GetHandle());
}
int RunMainRunLoopTimeout(double timeoutSeconds) {
int RunMainRunLoopTimeout(double timeout) {
wpi::Event& event = GetInstance();
bool timedOut = false;
bool signaled =
wpi::WaitForObject(event.GetHandle(), timeoutSeconds, &timedOut);
bool signaled = wpi::WaitForObject(event.GetHandle(), timeout, &timedOut);
if (timedOut) {
return 3;
}
@@ -35,4 +35,5 @@ void StopMainRunLoop() {
wpi::Event& event = GetInstance();
event.Set();
}
} // namespace cs

View File

@@ -8,6 +8,7 @@
#import <Foundation/Foundation.h>
namespace cs {
void RunMainRunLoop() {
if (CFRunLoopGetMain() != CFRunLoopGetCurrent()) {
NSLog(@"This method can only be called from the main thread");
@@ -16,15 +17,16 @@ void RunMainRunLoop() {
CFRunLoopRun();
}
int RunMainRunLoopTimeout(double timeoutSeconds) {
int RunMainRunLoopTimeout(double timeout) {
if (CFRunLoopGetMain() != CFRunLoopGetCurrent()) {
NSLog(@"This method can only be called from the main thread");
return -1;
}
return CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeoutSeconds, false);
return CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, false);
}
void StopMainRunLoop() {
CFRunLoopStop(CFRunLoopGetMain());
}
}

View File

@@ -12,6 +12,8 @@
#include <string>
#include <optional>
#include <wpi/StringMap.h>
#include "SourceImpl.h"
namespace cs {
@@ -88,9 +90,34 @@ class UsbCameraImpl : public SourceImpl {
UsbCameraImplObjc* cppGetObjc() { return m_objc; }
int CreatePropertyPublic(std::string_view name, std::function<std::unique_ptr<PropertyImpl>()> newFunc) {
return CreateProperty(name, newFunc);
}
PropertyImpl* GetPropertyPublic(int property) {
return GetProperty(property);
}
void NotifyPropertyCreatedPublic(int propIndex, PropertyImpl& prop) {
NotifyPropertyCreated(propIndex, prop);
}
void UpdatePropertyValuePublic(int property, bool setString, int value, std::string_view valueStr) {
UpdatePropertyValue(property, setString, value, valueStr);
}
wpi::mutex& GetMutex() { return m_mutex; }
// Property cache accessors
wpi::StringMap<uint32_t>& GetPropertyCache() { return m_propertyCache; }
wpi::StringMap<uint32_t>& GetPropertyAutoCache() { return m_propertyAutoCache; }
private:
UsbCameraImplObjc* m_objc;
std::vector<CameraModeStore> m_platformModes;
VideoMode m_mode;
// Property caches
wpi::StringMap<uint32_t> m_propertyCache;
wpi::StringMap<uint32_t> m_propertyAutoCache;
};
} // namespace cs

View File

@@ -5,11 +5,39 @@
#pragma once
#import <AVFoundation/AVFoundation.h>
#import "UsbCameraDelegate.h"
#include <memory>
#include <string_view>
#import "UsbCameraDelegate.h"
#import "UvcControlImpl.h"
#include "cscore_cpp.h"
// Quirk: exposure auto is 3 for on, 1 for off
#define kPropertyAutoExposureOn 3
#define kPropertyAutoExposureOff 1
// Property names
#define kPropertyBrightness "brightness"
#define kPropertyWhiteBalance "white_balance_temperature"
#define kPropertyExposure "raw_exposure_time_absolute"
#define kPropertyContrast "raw_contrast"
#define kPropertySaturation "raw_saturation"
#define kPropertySharpness "raw_sharpness"
#define kPropertyGain "gain"
#define kPropertyGamma "gamma"
#define kPropertyHue "raw_hue"
#define kPropertyFocus "focus_absolute"
#define kPropertyZoom "zoom"
#define kPropertyBackLightCompensation "backlight_compensation"
#define kPropertyPowerLineFrequency "power_line_frequency"
// Auto property names
#define kPropertyAutoExposure "exposure_auto"
#define kPropertyAutoWhiteBalance "white_balance_automatic"
#define kPropertyAutoFocus "focus_auto"
namespace cs {
class UsbCameraImpl;
}
@@ -30,6 +58,7 @@ class UsbCameraImpl;
@property(nonatomic) AVCaptureDevice* videoDevice;
@property(nonatomic) AVCaptureDeviceInput* videoInput;
@property(nonatomic) UsbCameraDelegate* callback;
@property(nonatomic) UvcControlImpl* uvcControl;
@property(nonatomic) AVCaptureVideoDataOutput* videoOutput;
@property(nonatomic) AVCaptureSession* session;
@@ -68,4 +97,8 @@ class UsbCameraImpl;
- (void)getCameraName:(std::string*)name;
- (void)setNewCameraPath:(std::string_view*)path;
- (void)deviceCacheProperties;
- (void)cacheProperty:(uint32_t)propID withName:(NSString *)name;
- (void)cacheAutoProperty:(uint32_t)propID withName:(NSString *)baseName;
@end

View File

@@ -2,12 +2,14 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#import "UsbCameraImplObjc.h"
#include "UsbCameraImpl.h"
#include <wpi/SmallString.h>
#pragma GCC diagnostic ignored "-Wunused-parameter"
#import "UsbCameraImplObjc.h"
#include "Notifier.h"
#include "Log.h"
#include "UsbCameraImpl.h"
template <typename S, typename... Args>
inline void NamedLog(UsbCameraImplObjc* objc, unsigned int level,
@@ -104,44 +106,300 @@ using namespace cs;
name:AVCaptureDeviceWasDisconnectedNotification
object:nil];
[self deviceConnect];
[self deviceCacheProperties];
});
}
- (BOOL)getEnabledWithProperty:(int)property withValue:(int)value {
auto sharedThis = self.cppImpl.lock();
if (!sharedThis) {
return false;
}
// There is room for quirk handling improvement here, but I will leave it
// for now.
if (property == sharedThis->GetPropertyIndex(kPropertyAutoExposure)) {
return value == kPropertyAutoExposureOn;
}
return value != 0;
}
- (int)clampToPercent:(int)value {
if (value < 0) {
return 0;
}
if (value > 100) {
return 100;
}
return value;
}
- (int)percentageToRaw:(int)propID percentage:(int)percentage min:(int)min max:(int)max {
if (min == max) {
return min;
}
return min + (max - min) * percentage / 100;
}
- (BOOL)isPercentageProperty:(int)propID {
return propID == CAPPROPID_BRIGHTNESS ||
propID == CAPPROPID_CONTRAST ||
propID == CAPPROPID_SATURATION ||
propID == CAPPROPID_HUE ||
propID == CAPPROPID_SHARPNESS ||
propID == CAPPROPID_GAIN;
}
// Property functions
- (void)setProperty:(int)property
withValue:(int)value
status:(CS_Status*)status {
auto sharedThis = self.cppImpl.lock();
if (!sharedThis) {
*status = CS_INVALID_HANDLE;
return;
}
// Make sure properties are cached
if (!self.propertiesCached) {
[self deviceCacheProperties];
}
// Get the property name from the property index
wpi::SmallString<128> nameBuf;
std::string_view propName = sharedThis->GetPropertyName(property, nameBuf, status);
if (*status != 0) {
OBJCERROR("Failed to get property name for index {}", property);
return;
}
std::string nameStr(propName);
// Check if it's an auto property
auto& propertyAutoCache = sharedThis->GetPropertyAutoCache();
auto autoIt = propertyAutoCache.find(nameStr);
if (autoIt != propertyAutoCache.end()) {
uint32_t propID = autoIt->second;
bool enabled = [self getEnabledWithProperty:property withValue:value];
dispatch_async_and_wait(self.sessionQueue, ^{
if (self.uvcControl == nil) {
*status = CS_INVALID_PROPERTY;
return;
}
if (![self.uvcControl setAutoProperty:propID enabled:enabled status:status]) {
OBJCERROR("Failed to set auto property {} to {}",
nameStr, enabled);
return;
}
// Update property value
sharedThis->UpdatePropertyValuePublic(property, false, value, {});
});
return;
}
// Handle regular property
auto& propertyCache = sharedThis->GetPropertyCache();
auto it = propertyCache.find(nameStr);
if (it == propertyCache.end()) {
OBJCERROR("Property not found in cache: {}", nameStr);
*status = CS_INVALID_PROPERTY;
return;
}
uint32_t propID = it->second;
dispatch_async_and_wait(self.sessionQueue, ^{
if (self.uvcControl == nil) {
*status = CS_INVALID_PROPERTY;
return;
}
// Get the property implementation to access its limits
const PropertyImpl* prop = sharedThis->GetPropertyPublic(property);
if (!prop) {
*status = CS_INVALID_PROPERTY;
return;
}
int32_t realValue = value;
if ([self isPercentageProperty:propID]) {
// Clamp to 0-100
realValue = [self clampToPercent:realValue];
// Scale to min/max
realValue = [self percentageToRaw:propID percentage:realValue min:prop->minimum max:prop->maximum];
}
if (![self.uvcControl setProperty:propID withValue:realValue status:status]) {
OBJCERROR("Failed to set property {} to value {}", nameStr, realValue);
return;
}
// Update property value in the container
sharedThis->UpdatePropertyValuePublic(property, false, value, {});
});
}
- (void)setStringProperty:(int)property
withValue:(std::string_view*)value
status:(CS_Status*)status {
*status = CS_INVALID_PROPERTY;
return;
}
// Standard common camera properties
- (void)setBrightness:(int)brightness status:(CS_Status*)status {
*status = CS_INVALID_PROPERTY;
auto sharedThis = self.cppImpl.lock();
if (!sharedThis) {
*status = CS_INVALID_HANDLE;
return;
}
// Make sure properties are cached
if (!self.propertiesCached) {
[self deviceCacheProperties];
}
// Get the property index and set it
int prop = sharedThis->GetPropertyIndex(kPropertyBrightness);
sharedThis->SetProperty(prop, brightness, status);
}
- (int)getBrightness:(CS_Status*)status {
*status = CS_INVALID_PROPERTY;
return 0;
auto sharedThis = self.cppImpl.lock();
if (!sharedThis) {
*status = CS_INVALID_HANDLE;
return 0;
}
// Make sure properties are cached
if (!self.propertiesCached) {
[self deviceCacheProperties];
}
// Get the property index and its value
int prop = sharedThis->GetPropertyIndex(kPropertyBrightness);
return sharedThis->GetProperty(prop, status);
}
- (void)setWhiteBalanceAuto:(CS_Status*)status {
*status = CS_INVALID_PROPERTY;
auto sharedThis = self.cppImpl.lock();
if (!sharedThis) {
*status = CS_INVALID_HANDLE;
return;
}
// Make sure properties are cached
if (!self.propertiesCached) {
[self deviceCacheProperties];
}
int prop = sharedThis->GetPropertyIndex(kPropertyAutoWhiteBalance);
sharedThis->SetProperty(prop, 1, status);
}
- (void)setWhiteBalanceHoldCurrent:(CS_Status*)status {
*status = CS_INVALID_PROPERTY;
auto sharedThis = self.cppImpl.lock();
if (!sharedThis) {
*status = CS_INVALID_HANDLE;
return;
}
// Make sure properties are cached
if (!self.propertiesCached) {
[self deviceCacheProperties];
}
int prop = sharedThis->GetPropertyIndex(kPropertyAutoWhiteBalance);
sharedThis->SetProperty(prop, 0, status);
}
- (void)setWhiteBalanceManual:(int)value status:(CS_Status*)status {
*status = CS_INVALID_PROPERTY;
auto sharedThis = self.cppImpl.lock();
if (!sharedThis) {
*status = CS_INVALID_HANDLE;
return;
}
// Make sure properties are cached
if (!self.propertiesCached) {
[self deviceCacheProperties];
}
// First disable auto white balance
int autoProp = sharedThis->GetPropertyIndex(kPropertyAutoWhiteBalance);
sharedThis->SetProperty(autoProp, 0, status);
if (*status != 0) {
return;
}
// Then set the white balance value
int prop = sharedThis->GetPropertyIndex(kPropertyWhiteBalance);
sharedThis->SetProperty(prop, value, status);
}
- (void)setExposureAuto:(CS_Status*)status {
*status = CS_INVALID_PROPERTY;
auto sharedThis = self.cppImpl.lock();
if (!sharedThis) {
*status = CS_INVALID_HANDLE;
return;
}
// Make sure properties are cached
if (!self.propertiesCached) {
[self deviceCacheProperties];
}
// Set the auto exposure property to enabled (1)
int prop = sharedThis->GetPropertyIndex(kPropertyAutoExposure);
sharedThis->SetProperty(prop, kPropertyAutoExposureOn, status);
}
- (void)setExposureHoldCurrent:(CS_Status*)status {
*status = CS_INVALID_PROPERTY;
auto sharedThis = self.cppImpl.lock();
if (!sharedThis) {
*status = CS_INVALID_HANDLE;
return;
}
// Make sure properties are cached
if (!self.propertiesCached) {
[self deviceCacheProperties];
}
// Set the auto exposure property to disabled (0)
int prop = sharedThis->GetPropertyIndex(kPropertyAutoExposure);
sharedThis->SetProperty(prop, kPropertyAutoExposureOff, status);
}
- (void)setExposureManual:(int)value status:(CS_Status*)status {
*status = CS_INVALID_PROPERTY;
auto sharedThis = self.cppImpl.lock();
if (!sharedThis) {
*status = CS_INVALID_HANDLE;
return;
}
// Make sure properties are cached
if (!self.propertiesCached) {
[self deviceCacheProperties];
}
// First disable auto exposure
int autoProp = sharedThis->GetPropertyIndex(kPropertyAutoExposure);
sharedThis->SetProperty(autoProp, kPropertyAutoExposureOff, status);
if (*status != 0) {
return;
}
// Then set the exposure value
int prop = sharedThis->GetPropertyIndex(kPropertyExposure);
sharedThis->SetProperty(prop, value, status);
}
- (bool)setVideoMode:(const cs::VideoMode&)mode status:(CS_Status*)status {
@@ -295,10 +553,144 @@ using namespace cs;
// All above are called from C++, must always dispatch to loop
// Property caching methods
- (void)deviceCacheProperties {
if (self.session == nil) {
return;
}
if (self.uvcControl == nil) {
return;
}
auto sharedThis = self.cppImpl.lock();
if (!sharedThis) {
OBJCERROR("Cannot cache properties: UsbCameraImpl not available");
return;
}
// Cache basic properties
[self cacheProperty:CAPPROPID_BRIGHTNESS withName:@kPropertyBrightness];
[self cacheProperty:CAPPROPID_WHITEBALANCE withName:@kPropertyWhiteBalance];
[self cacheProperty:CAPPROPID_EXPOSURE withName:@kPropertyExposure];
[self cacheProperty:CAPPROPID_CONTRAST withName:@kPropertyContrast];
[self cacheProperty:CAPPROPID_SATURATION withName:@kPropertySaturation];
[self cacheProperty:CAPPROPID_SHARPNESS withName:@kPropertySharpness];
[self cacheProperty:CAPPROPID_GAIN withName:@kPropertyGain];
[self cacheProperty:CAPPROPID_GAMMA withName:@kPropertyGamma];
[self cacheProperty:CAPPROPID_HUE withName:@kPropertyHue];
[self cacheProperty:CAPPROPID_FOCUS withName:@kPropertyFocus];
[self cacheProperty:CAPPROPID_ZOOM withName:@kPropertyZoom];
[self cacheProperty:CAPPROPID_BACKLIGHTCOMP withName:@kPropertyBackLightCompensation];
[self cacheProperty:CAPPROPID_POWERLINEFREQ withName:@kPropertyPowerLineFrequency];
// Cache auto properties
[self cacheAutoProperty:CAPPROPID_EXPOSURE withName:@kPropertyAutoExposure];
[self cacheAutoProperty:CAPPROPID_WHITEBALANCE withName:@kPropertyAutoWhiteBalance];
[self cacheAutoProperty:CAPPROPID_FOCUS withName:@kPropertyAutoFocus];
self.propertiesCached = true;
}
- (void)cacheProperty:(uint32_t)propID withName:(NSString *)name {
auto sharedThis = self.cppImpl.lock();
if (!sharedThis) {
OBJCERROR("Cannot cache property: UsbCameraImpl not available");
return;
}
if (self.uvcControl == nil) {
OBJCWARNING("Cannot cache property {}: UVC control not initialized", [name UTF8String]);
return;
}
// Get property limits
int32_t minimum = 0, maximum = 0, defaultValue = 0;
int32_t value = defaultValue;
CS_Status status;
std::string nameStr = std::string([name UTF8String]);
// Get the property limits
if (![self.uvcControl getPropertyLimits:propID
min:&minimum
max:&maximum
defValue:&defaultValue
status:&status]) {
OBJCWARNING("Failed to get property limits for {}", nameStr);
return;
}
// Get current value
if (![self.uvcControl getProperty:propID withValue:&value status:&status]) {
value = defaultValue;
OBJCWARNING("Failed to get current value for {}: {}",
nameStr, value);
return;
}
// Create property
auto& propertyCache = sharedThis->GetPropertyCache();
propertyCache[nameStr] = propID;
// Create the property implementation
std::unique_ptr<PropertyImpl> prop;
prop = std::make_unique<PropertyImpl>(nameStr);
prop->propKind = CS_PROP_INTEGER;
prop->value = value;
prop->minimum = minimum;
prop->maximum = maximum;
prop->step = 1; // Most camera properties use a step of 1
prop->defaultValue = defaultValue;
// Add the property to the container
std::scoped_lock lock(sharedThis->GetMutex());
int ndx = sharedThis->CreatePropertyPublic(nameStr, [&] { return std::move(prop); });
// Notify that property has been created
sharedThis->NotifyPropertyCreatedPublic(ndx, *sharedThis->GetPropertyPublic(ndx));
}
- (void)cacheAutoProperty:(uint32_t)propID withName:(NSString *)baseName {
auto sharedThis = self.cppImpl.lock();
if (!sharedThis) {
OBJCERROR("Cannot cache auto property: UsbCameraImpl not available");
return;
}
if (self.uvcControl == nil) {
OBJCWARNING("Cannot cache auto property {}: UVC control not initialized", [baseName UTF8String]);
return;
}
// Build auto mode property name
std::string nameStr = std::string([baseName UTF8String]);
// Get current auto mode status
bool enabled = false;
CS_Status status = 0;
if(![self.uvcControl getAutoProperty:propID enabled:&enabled status:&status]) {
OBJCWARNING("Failed to get auto property {}", nameStr);
return;
}
// Create property
std::unique_ptr<PropertyImpl> prop;
prop = std::make_unique<PropertyImpl>(nameStr);
prop->propKind = CS_PROP_BOOLEAN;
prop->value = enabled ? 1 : 0;
prop->minimum = 0;
prop->maximum = 1;
prop->step = 1;
prop->defaultValue = 0; // Default is manual mode
// Add property to container
std::scoped_lock lock(sharedThis->GetMutex());
int ndx = sharedThis->CreatePropertyPublic(nameStr, [&] { return std::move(prop); });
// Notify property created
sharedThis->NotifyPropertyCreatedPublic(ndx, *sharedThis->GetPropertyPublic(ndx));
// Map property name to ID
auto& propertyAutoCache = sharedThis->GetPropertyAutoCache();
propertyAutoCache[nameStr] = propID;
}
static cs::VideoMode::PixelFormat FourCCToPixelFormat(FourCharCode fourcc) {
@@ -380,22 +772,27 @@ static cs::VideoMode::PixelFormat FourCCToPixelFormat(FourCharCode fourcc) {
toCheck->height, toCheck->fps);
std::vector<CameraModeStore>& platformModes =
sharedThis->objcGetPlatformVideoModes();
// Find the matching mode
auto match = std::find_if(platformModes.begin(), platformModes.end(),
[&](CameraModeStore& input) {
return input.mode.CompareWithoutFps(*toCheck);
});
// Find all matching modes
std::vector<CameraModeStore*> matchingModes;
for (auto& mode : platformModes) {
if (mode.mode.CompareWithoutFps(*toCheck)) {
matchingModes.push_back(&mode);
}
}
if (match == platformModes.end()) {
if (matchingModes.empty()) {
return nil;
}
// Check FPS
for (CameraFPSRange& range : match->fpsRanges) {
OBJCDEBUG3("Checking Range {} {}", range.min, range.max);
if (range.IsWithinRange(toCheck->fps)) {
*fps = toCheck->fps;
return match->format;
for (auto mode : matchingModes) {
for (CameraFPSRange& range : mode->fpsRanges) {
OBJCDEBUG3("Checking Range {} {}", range.min, range.max);
if (range.IsWithinRange(toCheck->fps)) {
*fps = toCheck->fps;
return mode->format;
}
}
}
@@ -454,6 +851,53 @@ static cs::VideoMode::PixelFormat FourCCToPixelFormat(FourCharCode fourcc) {
self.deviceValid = true;
}
- (CMTime)findNearestFrameDuration:(int)fps {
if (self.currentFormat == nil) {
return CMTimeMake(1, fps);
}
NSArray<AVFrameRateRange*>* frameRates = self.currentFormat.videoSupportedFrameRateRanges;
if (frameRates.count == 0) {
return CMTimeMake(1, fps);
}
// Find the nearest frame duration
CMTime nearestDuration = CMTimeMake(1, fps);
double minDiff = DBL_MAX;
for (AVFrameRateRange* range in frameRates) {
CMTime minDuration = range.minFrameDuration;
CMTime maxDuration = range.maxFrameDuration;
// Calculate frame duration for current fps
CMTime targetDuration = CMTimeMake(1, fps);
// Check if within range
if (CMTimeCompare(targetDuration, minDuration) >= 0 &&
CMTimeCompare(targetDuration, maxDuration) <= 0) {
return targetDuration;
}
// Calculate difference with min value
double minDiffValue = fabs(CMTimeGetSeconds(targetDuration) - CMTimeGetSeconds(minDuration));
if (minDiffValue < minDiff) {
minDiff = minDiffValue;
nearestDuration = minDuration;
}
// Calculate difference with max value
double maxDiffValue = fabs(CMTimeGetSeconds(targetDuration) - CMTimeGetSeconds(maxDuration));
if (maxDiffValue < minDiff) {
minDiff = maxDiffValue;
nearestDuration = maxDuration;
}
}
OBJCDEBUG("Nearest fps: {}", nearestDuration.timescale / static_cast<double>(nearestDuration.value));
return nearestDuration;
}
- (bool)deviceStreamOn {
if (self.streaming) {
return false;
@@ -461,24 +905,32 @@ static cs::VideoMode::PixelFormat FourCCToPixelFormat(FourCharCode fourcc) {
if (!self.deviceValid) {
return false;
}
self.streaming = true;
if (![self.videoDevice lockForConfiguration:nil]) {
OBJCERROR("Failed to lock for configuration");
return false;
}
[self.session beginConfiguration];
if (self.currentFormat != nil) {
self.videoDevice.activeFormat = self.currentFormat;
}
if (self.currentFPS != 0) {
CMTime frameDuration = [self findNearestFrameDuration:self.currentFPS];
self.videoDevice.activeVideoMinFrameDuration = frameDuration;
self.videoDevice.activeVideoMaxFrameDuration = frameDuration;
}
[self.session commitConfiguration];
self.streaming = true;
// Start the capture session before device unlock to ensure
// the session preset settings are preserved
[self.session startRunning];
if ([self.videoDevice lockForConfiguration:nil]) {
if (self.currentFormat != nil) {
self.videoDevice.activeFormat = self.currentFormat;
}
if (self.currentFPS != 0) {
self.videoDevice.activeVideoMinFrameDuration =
CMTimeMake(1, self.currentFPS);
self.videoDevice.activeVideoMaxFrameDuration =
CMTimeMake(1, self.currentFPS);
}
[self.videoDevice unlockForConfiguration];
} else {
OBJCERROR("Failed to lock for configuration");
}
[self.videoDevice unlockForConfiguration];
return true;
}
@@ -569,6 +1021,16 @@ static cs::VideoMode::PixelFormat FourCCToPixelFormat(FourCharCode fourcc) {
goto err;
}
CS_Status status;
self.uvcControl = [UvcControlImpl createFromAVCaptureDevice:self.videoDevice status:&status];
if (self.uvcControl == nil) {
OBJCWARNING("Failed to initialize UVC control for camera: {}", status);
} else {
OBJCINFO("UVC control initialized successfully");
}
self.uvcControl.cppImpl = self.cppImpl;
self.callback = [[UsbCameraDelegate alloc] init];
if (self.callback == nil) {
OBJCWARNING("Creating Camera Callback failed");

View File

@@ -0,0 +1,129 @@
// 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
#import <AVFoundation/AVFoundation.h>
#import <IOKit/IOCFPlugIn.h>
#import <IOKit/usb/IOUSBLib.h>
#include <memory>
#include <string_view>
#import "UsbCameraDelegate.h"
#include "cscore_cpp.h"
// Status code definition
#define CS_UVC_STATUS_ERROR -3001
#define CS_UVC_STATUS_DEVICE_DISCONNECTED -3002
// UVC control selector definitions
#define UVC_INPUT_TERMINAL_ID 0x01
// Camera terminal control selectors
#define CT_AE_MODE_CONTROL 0x02
#define CT_AE_PRIORITY_CONTROL 0x03
#define CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04
#define CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05
#define CT_FOCUS_ABSOLUTE_CONTROL 0x06
#define CT_FOCUS_RELATIVE_CONTROL 0x07
#define CT_FOCUS_AUTO_CONTROL 0x08
#define CT_ZOOM_ABSOLUTE_CONTROL 0x0B
#define CT_ZOOM_RELATIVE_CONTROL 0x0C
// Processing unit control selectors
#define PU_BACKLIGHT_COMPENSATION_CONTROL 0x01
#define PU_BRIGHTNESS_CONTROL 0x02
#define PU_CONTRAST_CONTROL 0x03
#define PU_GAIN_CONTROL 0x04
#define PU_POWER_LINE_FREQUENCY_CONTROL 0x05
#define PU_HUE_CONTROL 0x06
#define PU_SATURATION_CONTROL 0x07
#define PU_SHARPNESS_CONTROL 0x08
#define PU_GAMMA_CONTROL 0x09
#define PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0A
#define PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0B
#define PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0C
#define PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0D
#define PU_HUE_AUTO_CONTROL 0x10
#define PU_CONTRAST_AUTO_CONTROL 0x13
// Camera request error code
#define VC_REQUEST_ERROR_CODE_CONTROL 0x02
// UVC control interface definitions
#define UVC_CONTROL_INTERFACE_CLASS 14
#define UVC_CONTROL_INTERFACE_SUBCLASS 1
// UVC control request types
#define UVC_SET_CUR 0x01
#define UVC_GET_CUR 0x81
#define UVC_GET_MIN 0x82
#define UVC_GET_MAX 0x83
#define UVC_GET_RES 0x84
#define UVC_GET_INFO 0x86
#define UVC_GET_DEF 0x87
// Camera property ID definitions
#define CAPPROPID_EXPOSURE 1
#define CAPPROPID_FOCUS 2
#define CAPPROPID_ZOOM 3
#define CAPPROPID_WHITEBALANCE 4
#define CAPPROPID_GAIN 5
#define CAPPROPID_BRIGHTNESS 6
#define CAPPROPID_CONTRAST 7
#define CAPPROPID_SATURATION 8
#define CAPPROPID_GAMMA 9
#define CAPPROPID_HUE 10
#define CAPPROPID_SHARPNESS 11
#define CAPPROPID_BACKLIGHTCOMP 12
#define CAPPROPID_POWERLINEFREQ 13
#define CAPPROPID_LAST 14
namespace cs {
class UsbCameraImpl;
}
@interface UvcControlImpl : NSObject
@property(nonatomic) IOUSBInterfaceInterface190** controlInterface;
@property(nonatomic) uint32_t processingUnitID;
@property(nonatomic) std::weak_ptr<cs::UsbCameraImpl> cppImpl;
// Create from AVCaptureDevice
+ (instancetype)createFromAVCaptureDevice:(AVCaptureDevice*)device status:(CS_Status*)status;
// Initialize with USB vendor/product/location
- (instancetype)initWithVendorId:(uint16_t)vid
productId:(uint16_t)pid
location:(uint32_t)location
status:(CS_Status*)status;
- (void)dealloc;
// Basic property control
- (bool)setProperty:(uint32_t)propID
withValue:(int32_t)value
status:(CS_Status*)status;
- (bool)getProperty:(uint32_t)propID
withValue:(int32_t*)value
status:(CS_Status*)status;
// Auto mode control
- (bool)setAutoProperty:(uint32_t)propID
enabled:(bool)enabled
status:(CS_Status*)status;
- (bool)getAutoProperty:(uint32_t)propID
enabled:(bool*)enabled
status:(CS_Status*)status;
// Property range query
- (bool)getPropertyLimits:(uint32_t)propID
min:(int32_t*)min
max:(int32_t*)max
defValue:(int32_t*)defValue
status:(CS_Status*)status;
@end

View File

@@ -0,0 +1,773 @@
// 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.
// Copyright (c) 2017 Jason von Nieda, Niels Moseley
//
// The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <AVFoundation/AVFoundation.h>
#import "UvcControlImpl.h"
#include "Log.h"
#include "UsbCameraImpl.h"
template <typename S, typename... Args>
inline void NamedLog(UvcControlImpl* objc, unsigned int level,
const char* file, unsigned int line, const S& format,
Args&&... args) {
auto sharedThis = objc.cppImpl.lock();
if (!sharedThis) {
return;
}
wpi::Logger& logger = sharedThis->objcGetLogger();
std::string_view name = sharedThis->GetName();
if (logger.HasLogger() && level >= logger.min_level()) {
cs::NamedLogV(logger, level, file, line, name, format,
fmt::make_format_args(args...));
}
}
#define UVCLOG(level, format, ...) \
NamedLog(self, level, __FILE__, __LINE__, \
format __VA_OPT__(, ) __VA_ARGS__)
#define UVCERROR(format, ...) \
UVCLOG(::wpi::WPI_LOG_ERROR, format __VA_OPT__(, ) __VA_ARGS__)
#define UVCWARNING(format, ...) \
UVCLOG(::wpi::WPI_LOG_WARNING, format __VA_OPT__(, ) __VA_ARGS__)
#define UVCINFO(format, ...) \
UVCLOG(::wpi::WPI_LOG_INFO, format __VA_OPT__(, ) __VA_ARGS__)
#ifdef NDEBUG
#define UVCDEBUG(format, ...) \
do { \
} while (0)
#define UVCDEBUG1(format, ...) \
do { \
} while (0)
#define UVCDEBUG2(format, ...) \
do { \
} while (0)
#define UVCDEBUG3(format, ...) \
do { \
} while (0)
#define UVCDEBUG4(format, ...) \
do { \
} while (0)
#else
#define UVCDEBUG(format, ...) \
UVCLOG(::wpi::WPI_LOG_DEBUG, format __VA_OPT__(, ) __VA_ARGS__)
#define UVCDEBUG1(format, ...) \
UVCLOG(::wpi::WPI_LOG_DEBUG1, format __VA_OPT__(, ) __VA_ARGS__)
#define UVCDEBUG2(format, ...) \
UVCLOG(::wpi::WPI_LOG_DEBUG2, format __VA_OPT__(, ) __VA_ARGS__)
#define UVCDEBUG3(format, ...) \
UVCLOG(::wpi::WPI_LOG_DEBUG3, format __VA_OPT__(, ) __VA_ARGS__)
#define UVCDEBUG4(format, ...) \
UVCLOG(::wpi::WPI_LOG_DEBUG4, format __VA_OPT__(, ) __VA_ARGS__)
#endif
// USB descriptor for UVC processing unit
struct ProcessingUnitDescriptor
{
uint8_t bLength;
uint8_t bDescriptorType; // CS_INTERFACE 0x24
uint8_t bDescriptorSubtype; // VC_PROCESSING_UNIT 0x05
uint8_t bUnitID;
};
struct propertyInfo_t
{
uint32_t selector; // selector ID
uint32_t unit; // unit (==0 for INPUT TERMINA:, ==1 for PROCESSING UNIT)
uint32_t length; // length (bytes)
};
/** The order of the propertyInfo structure must
be the same as the PROPID numbers in the
openpnp-capture.h header */
const propertyInfo_t propertyInfo[] =
{
{0,0,0},
{CT_EXPOSURE_TIME_ABSOLUTE_CONTROL , 0, 4},
{CT_FOCUS_ABSOLUTE_CONTROL , 0, 2},
{CT_ZOOM_ABSOLUTE_CONTROL , 0, 2},
{PU_WHITE_BALANCE_TEMPERATURE_CONTROL, 1, 2},
{PU_GAIN_CONTROL , 1, 2},
{PU_BRIGHTNESS_CONTROL , 1, 2},
{PU_CONTRAST_CONTROL , 1, 2},
{PU_SATURATION_CONTROL , 1, 2},
{PU_GAMMA_CONTROL , 1, 2},
{PU_HUE_CONTROL , 1, 2},
{PU_SHARPNESS_CONTROL , 1, 2},
{PU_BACKLIGHT_COMPENSATION_CONTROL , 1, 2},
{PU_POWER_LINE_FREQUENCY_CONTROL , 1, 1}
};
@implementation UvcControlImpl {
IOUSBDeviceInterface** _deviceInterface;
}
+ (instancetype)createFromAVCaptureDevice:(AVCaptureDevice*)device status:(CS_Status*)status {
if (!device) {
NSLog(@"UVC: device is nil");
*status = CS_UVC_STATUS_ERROR;
return nil;
}
NSError* error = nil;
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"^UVC\\s+Camera\\s+VendorID\\_([0-9]+)\\s+ProductID\\_([0-9]+)$"
options:0
error:&error];
if (error) {
NSLog(@"UVC: failed to create regex: %@", error);
*status = CS_UVC_STATUS_ERROR;
return nil;
}
NSString* modelID = [device valueForKey:@"modelID"];
if (!modelID) {
NSLog(@"UVC: modelID is nil");
*status = CS_UVC_STATUS_ERROR;
return nil;
}
NSTextCheckingResult* match = [regex firstMatchInString:modelID
options:0
range:NSMakeRange(0, modelID.length)];
if (!match || match.numberOfRanges != 3) {
NSLog(@"UVC: modelID regex match failed");
*status = CS_UVC_STATUS_ERROR;
return nil;
}
NSString* vendorIDStr = [modelID substringWithRange:[match rangeAtIndex:1]];
NSString* productIDStr = [modelID substringWithRange:[match rangeAtIndex:2]];
uint16_t vendorID = (uint16_t)strtoul([vendorIDStr UTF8String], NULL, 10);
uint16_t productID = (uint16_t)strtoul([productIDStr UTF8String], NULL, 10);
uint32_t locationID = 0;
CFMutableDictionaryRef dict = IOServiceMatching(kIOUSBDeviceClassName);
CFDictionarySetValue(dict, CFSTR("idVendor"), (__bridge CFNumberRef)@(vendorID));
CFDictionarySetValue(dict, CFSTR("idProduct"), (__bridge CFNumberRef)@(productID));
io_iterator_t iter = 0;
kern_return_t ioResult = IOServiceGetMatchingServices(kIOMainPortDefault, dict, &iter);
if (ioResult == kIOReturnSuccess) {
io_service_t usbDevice = IOIteratorNext(iter);
while (usbDevice != 0) {
CFTypeRef locationIDRef = IORegistryEntryCreateCFProperty(usbDevice,
CFSTR("locationID"),
kCFAllocatorDefault,
0);
if (locationIDRef) {
locationID = [(__bridge NSNumber*)locationIDRef unsignedIntValue];
CFRelease(locationIDRef);
NSString* uniqueID = [device valueForKey:@"uniqueID"];
NSString* locationIDHex = [NSString stringWithFormat:@"0x%x", locationID];
if ([uniqueID hasPrefix:locationIDHex]) {
IOObjectRelease(usbDevice);
break;
}
}
IOObjectRelease(usbDevice);
usbDevice = IOIteratorNext(iter);
}
IOObjectRelease(iter);
}
UvcControlImpl *instance = [[UvcControlImpl alloc] initWithVendorId:vendorID
productId:productID
location:locationID
status:status];
if (!instance) {
NSLog(@"UVC: failed to create UvcControlImpl, status=%d", *status);
}
return instance;
}
- (instancetype)initWithVendorId:(uint16_t)vid
productId:(uint16_t)pid
location:(uint32_t)location
status:(CS_Status*)status {
self = [super init];
if (self) {
// UVCINFO("Initializing with VID: 0x{:04X}, PID: 0x{:04X}, Location: 0x{:08X}", vid, pid, location);
_deviceInterface = [self findDevice:vid productId:pid location:location];
if (_deviceInterface == nullptr) {
// UVCWARNING("Failed to find device");
*status = CS_UVC_STATUS_DEVICE_DISCONNECTED;
return nil;
}
_processingUnitID = [self getProcessingUnitID:_deviceInterface];
_controlInterface = [self createControlInterface:_deviceInterface];
if (_controlInterface == nullptr) {
// UVCWARNING("Failed to create control interface");
*status = CS_UVC_STATUS_DEVICE_DISCONNECTED;
return nil;
}
}
return self;
}
- (void)dealloc {
if (_controlInterface != nullptr) {
(*_controlInterface)->USBInterfaceClose(_controlInterface);
(*_controlInterface)->Release(_controlInterface);
}
if (_deviceInterface != nullptr) {
(*_deviceInterface)->Release(_deviceInterface);
}
}
- (IOUSBDeviceInterface**)findDevice:(uint16_t)vid
productId:(uint16_t)pid
location:(uint32_t)location {
CFMutableDictionaryRef dict = IOServiceMatching(kIOUSBDeviceClassName);
io_iterator_t serviceIterator;
kern_return_t result = IOServiceGetMatchingServices((mach_port_t)NULL, dict, &serviceIterator);
if (result != kIOReturnSuccess) {
UVCERROR("findDevice: IOServiceGetMatchingServices failed: {}", result);
return nullptr;
}
io_service_t device;
while((device = IOIteratorNext(serviceIterator)) != 0) {
IOUSBDeviceInterface **deviceInterface = nullptr;
IOCFPlugInInterface **plugInInterface = nullptr;
SInt32 score;
kern_return_t result = IOCreatePlugInInterfaceForService(
device, kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID, &plugInInterface, &score);
if ((result != kIOReturnSuccess) || (plugInInterface == nullptr)) {
UVCERROR("findDevice: Camera control error: {}", result);
IOObjectRelease(device);
continue;
}
HRESULT hr = (*plugInInterface)->QueryInterface(plugInInterface,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
(LPVOID*)&deviceInterface);
if (hr || (deviceInterface == nullptr)) {
(*plugInInterface)->Release(plugInInterface);
IOObjectRelease(device);
UVCERROR("findDevice: QueryInterface failed");
continue;
}
uint16_t vendorID, productID;
uint32_t locationID;
result = (*deviceInterface)->GetDeviceVendor(deviceInterface, &vendorID);
result = (*deviceInterface)->GetDeviceProduct(deviceInterface, &productID);
result = (*deviceInterface)->GetLocationID(deviceInterface, &locationID);
// if 'location' is zero, we won't match on location
// to achieve this, we simply set locationID to zero.
if (location == 0) {
locationID = 0;
}
if ((vendorID == vid) && (productID == pid) && (locationID == location)) {
(*plugInInterface)->Release(plugInInterface);
IOObjectRelease(device);
IOObjectRelease(serviceIterator);
return deviceInterface;
}
(*deviceInterface)->Release(deviceInterface);
(*plugInInterface)->Release(plugInInterface);
IOObjectRelease(device);
}
IOObjectRelease(serviceIterator);
return nullptr;
}
- (uint32_t)getProcessingUnitID:(IOUSBDeviceInterface**)dev {
IOReturn kr;
IOUSBConfigurationDescriptorPtr configDesc;
kr = (*dev)->GetConfigurationDescriptorPtr(dev, 0, &configDesc);
if (kr) {
return 0;
}
UVCDEBUG4("USB descriptor:");
UVCDEBUG4(" length = 0x{:08X}", configDesc->bLength);
UVCDEBUG4(" type = 0x{:08X}", configDesc->bDescriptorType);
UVCDEBUG4(" totalLen = 0x{:08X}", configDesc->wTotalLength);
UVCDEBUG4(" interfaces = 0x{:08X}", configDesc->bNumInterfaces);
uint32_t idx = 0;
uint8_t *ptr = (uint8_t*)configDesc;
// Search for VIDEO/CONTROL interface descriptor
// Class=14, Subclass=1, Protocol=0
// and find the processing unit, if available..
// DescriptorType 0x24, DescriptorSubType 0x5
IOUSBInterfaceDescriptor *iface = NULL;
ProcessingUnitDescriptor *pud = NULL;
bool inVideoControlInterfaceDescriptor = false;
while(idx < configDesc->wTotalLength) {
IOUSBDescriptorHeader *hdr = (IOUSBDescriptorHeader *)&ptr[idx];
switch(hdr->bDescriptorType)
{
case 0x05: // Endpoint descriptor ID
break;
case 0x02: // Configuration descriptor ID
break;
case 0x04: // Interface descriptor ID
iface = (IOUSBInterfaceDescriptor*)&ptr[idx];
if ((iface->bInterfaceClass == 14) &&
(iface->bInterfaceSubClass == 1) &&
(iface->bInterfaceProtocol == 0))
{
inVideoControlInterfaceDescriptor = true;
}
else
{
inVideoControlInterfaceDescriptor = false;
}
break;
case 0x24: // class-specific ID
pud = (ProcessingUnitDescriptor*)&ptr[idx];
if (inVideoControlInterfaceDescriptor)
{
if (pud->bDescriptorSubtype == 0x05)
{
return pud->bUnitID;
}
}
break;
default:
break;
}
idx += hdr->bLength;
}
return 0;
}
- (IOUSBInterfaceInterface190**)createControlInterface:(IOUSBDeviceInterface**)deviceInterface {
IOUSBInterfaceInterface190 **controlInterface;
io_iterator_t interfaceIterator;
IOUSBFindInterfaceRequest interfaceRequest;
interfaceRequest.bInterfaceClass = UVC_CONTROL_INTERFACE_CLASS;
interfaceRequest.bInterfaceSubClass = UVC_CONTROL_INTERFACE_SUBCLASS;
interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare;
IOReturn result = (*deviceInterface)->CreateInterfaceIterator(deviceInterface,
&interfaceRequest, &interfaceIterator);
if (result != kIOReturnSuccess) {
return nullptr;
}
io_service_t usbInterface;
if ((usbInterface = IOIteratorNext(interfaceIterator)) != 0) {
IOCFPlugInInterface **plugInInterface = nullptr;
SInt32 score;
kern_return_t kr = IOCreatePlugInInterfaceForService(usbInterface,
kIOUSBInterfaceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugInInterface,
&score);
kr = IOObjectRelease(usbInterface);
if ((kr != kIOReturnSuccess) || !plugInInterface) {
UVCERROR("createControlInterface: cannot create plug-in {:08X}",
kr);
return nullptr;
}
HRESULT hr = (*plugInInterface)->QueryInterface(plugInInterface,
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
(LPVOID*) &controlInterface);
(*plugInInterface)->Release(plugInInterface);
if (hr || !controlInterface) {
UVCERROR("createControlInterface: cannot create device interface {:08X}",
result);
return nullptr;
}
UVCDEBUG3("createControlInterface: created control interface");
return controlInterface;
}
return nullptr;
}
- (bool)sendControlRequest:(IOUSBDevRequest)req {
if (_controlInterface == nullptr) {
UVCERROR("control interface is NULL");
return false;
}
kern_return_t kr;
if (@available(macOS 12.0, *)) {
// macOS 12 doesn't like if we're trying to open USB interface here...
} else {
kr = (*_controlInterface)->USBInterfaceOpen(_controlInterface);
if (kr != kIOReturnSuccess) {
UVCERROR("USBInterfaceOpen failed with error: 0x{:08X}", kr);
return false;
}
}
kr = (*_controlInterface)->ControlRequest(_controlInterface, 0, &req);
if (kr != kIOReturnSuccess) {
// IOKIT error code
#define err_get_system(err) (((err)>>26)&0x3f)
#define err_get_sub(err) (((err)>>14)&0xfff)
#define err_get_code(err) ((err)&0x3fff)
uint32_t code = err_get_code(kr);
uint32_t sys = err_get_system(kr);
uint32_t sub = err_get_sub(kr);
switch(kr)
{
case kIOUSBUnknownPipeErr:
UVCERROR("Pipe ref not recognised");
break;
case kIOUSBTooManyPipesErr:
UVCERROR("Too many pipes");
break;
case kIOUSBEndpointNotFound:
UVCERROR("Endpoint not found");
break;
case kIOUSBConfigNotFound:
UVCERROR("USB configuration not found");
break;
case kIOUSBPipeStalled:
//Note: we don't report this as an error as this happens when
// an unsupported or locked property is set.
UVCDEBUG("Pipe has stalled, error needs to be cleared");
break;
case kIOUSBInterfaceNotFound:
UVCERROR("USB control interface not found");
break;
default:
UVCERROR("ControlRequest failed (KR=sys:sub:code) = {:02Xh}:{:03Xh}:{:04Xh}",
sys, sub, code);
break;
}
if (@available(macOS 12.0, *)) {
// macOS 12 doesn't like if we're trying to close USB interface here...
} else {
kr = (*_controlInterface)->USBInterfaceClose(_controlInterface);
if (kr != kIOReturnSuccess) {
UVCERROR("USBInterfaceClose failed");
}
}
return false;
}
if (@available(macOS 12.0, *)) {
// macOS 12 doesn't like if we're trying to close USB interface here either...
} else {
kr = (*_controlInterface)->USBInterfaceClose(_controlInterface);
if (kr != kIOReturnSuccess) {
UVCERROR("USBInterfaceClose failed");
}
}
return true;
}
- (bool)setData:(uint32_t)selector unit:(uint32_t)unit length:(uint32_t)length data:(int32_t)data {
IOUSBDevRequest req;
req.bmRequestType = USBmakebmRequestType((UInt8)kUSBOut, (UInt8)kUSBClass, (UInt8)kUSBInterface);
req.bRequest = UVC_SET_CUR;
req.wValue = (selector << 8);
req.wIndex = (unit << 8);
req.wLength = length;
req.wLenDone = 0;
req.pData = &data;
return [self sendControlRequest:req];
}
- (bool)getData:(uint32_t)selector unit:(uint32_t)unit length:(uint32_t)length data:(int32_t*)data {
IOUSBDevRequest req;
req.bmRequestType = USBmakebmRequestType((UInt8)kUSBIn, (UInt8)kUSBClass, (UInt8)kUSBInterface);
req.bRequest = UVC_GET_CUR;
req.wValue = (selector << 8);
req.wIndex = (unit << 8);
req.wLength = length;
req.wLenDone = 0;
req.pData = data;
return [self sendControlRequest:req];
}
- (bool)getMaxData:(uint32_t)selector unit:(uint32_t)unit length:(uint32_t)length data:(int32_t*)data {
IOUSBDevRequest req;
*data = 0;
req.bmRequestType = USBmakebmRequestType((UInt8)kUSBIn, (UInt8)kUSBClass, (UInt8)kUSBInterface);
req.bRequest = UVC_GET_MAX;
req.wValue = (selector << 8);
req.wIndex = (unit << 8);
req.wLength = length;
req.wLenDone = 0;
req.pData = data;
return [self sendControlRequest:req];
}
- (bool)getMinData:(uint32_t)selector unit:(uint32_t)unit length:(uint32_t)length data:(int32_t*)data {
IOUSBDevRequest req;
*data = 0;
req.bmRequestType = USBmakebmRequestType((UInt8)kUSBIn, (UInt8)kUSBClass, (UInt8)kUSBInterface);
req.bRequest = UVC_GET_MIN;
req.wValue = (selector << 8);
req.wIndex = (unit << 8);
req.wLength = length;
req.wLenDone = 0;
req.pData = data;
return [self sendControlRequest:req];
}
- (bool)getDefault:(uint32_t)selector unit:(uint32_t)unit length:(uint32_t)length data:(int32_t*)data {
IOUSBDevRequest req;
*data = 0;
req.bmRequestType = USBmakebmRequestType((UInt8)kUSBIn, (UInt8)kUSBClass, (UInt8)kUSBInterface);
req.bRequest = UVC_GET_DEF;
req.wValue = (selector << 8);
req.wIndex = (unit << 8);
req.wLength = length;
req.wLenDone = 0;
req.pData = data;
return [self sendControlRequest:req];
}
- (bool)getInfo:(uint32_t)selector unit:(uint32_t)unit data:(uint32_t*)data {
IOUSBDevRequest req;
*data = 0;
req.bmRequestType = USBmakebmRequestType((UInt8)kUSBIn, (UInt8)kUSBClass, (UInt8)kUSBInterface);
req.bRequest = UVC_GET_INFO;
req.wValue = (selector << 8);
req.wIndex = (unit << 8);
req.wLength = 1;
req.wLenDone = 0;
req.pData = data;
return [self sendControlRequest:req];
}
- (bool)setProperty:(uint32_t)propID withValue:(int32_t)value status:(CS_Status*)status {
if (_controlInterface == nullptr) {
UVCERROR("control interface is NULL");
*status = CS_UVC_STATUS_DEVICE_DISCONNECTED;
return false;
}
bool ok = false;
if (propID < CAPPROPID_LAST) {
uint32_t unit = (propertyInfo[propID].unit == 0) ? UVC_INPUT_TERMINAL_ID : _processingUnitID;
ok = [self setData:propertyInfo[propID].selector unit:unit length:propertyInfo[propID].length data:value];
if (!ok) {
UVCWARNING("Failed to set property {}", propID);
}
} else {
UVCWARNING("Invalid property ID: {}", propID);
}
return ok;
}
- (bool)getProperty:(uint32_t)propID withValue:(int32_t*)value status:(CS_Status*)status {
if (_controlInterface == nullptr) {
UVCERROR("control interface is NULL");
*status = CS_UVC_STATUS_DEVICE_DISCONNECTED;
return false;
}
bool ok = false;
if (propID < CAPPROPID_LAST) {
uint32_t unit = (propertyInfo[propID].unit == 0) ? UVC_INPUT_TERMINAL_ID : _processingUnitID;
ok = [self getData:propertyInfo[propID].selector unit:unit length:propertyInfo[propID].length data:value];
switch(propertyInfo[propID].length) {
case 2:
*value = static_cast<int16_t>(*value);
break;
case 1:
*value = static_cast<int8_t>(*value);
break;
default:
break;
}
if (!ok) {
UVCWARNING("Failed to get property {}", propID);
}
} else {
UVCWARNING("Invalid property ID: {}", propID);
}
return ok;
}
- (bool)setAutoProperty:(uint32_t)propID enabled:(bool)enabled status:(CS_Status*)status {
if (_controlInterface == nullptr) {
UVCERROR("control interface is NULL");
*status = CS_UVC_STATUS_DEVICE_DISCONNECTED;
return false;
}
int32_t value = enabled ? 1 : 0;
switch(propID) {
case CAPPROPID_EXPOSURE:
return [self setData:CT_AE_MODE_CONTROL unit:UVC_INPUT_TERMINAL_ID length:1 data:enabled ? 0x8 : 0x1];
case CAPPROPID_WHITEBALANCE:
return [self setData:PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL unit:_processingUnitID length:1 data:value];
case CAPPROPID_FOCUS:
return [self setData:CT_FOCUS_AUTO_CONTROL unit:UVC_INPUT_TERMINAL_ID length:1 data:value];
default:
return false;
}
}
- (bool)getAutoProperty:(uint32_t)propID enabled:(bool*)enabled status:(CS_Status*)status {
if (_controlInterface == nullptr) {
UVCERROR("control interface is NULL");
*status = CS_UVC_STATUS_DEVICE_DISCONNECTED;
return false;
}
int32_t value;
switch(propID) {
case CAPPROPID_EXPOSURE:
if ([self getData:CT_AE_MODE_CONTROL unit:UVC_INPUT_TERMINAL_ID length:1 data:&value]) {
// value = 1 -> manual mode
// 2 -> auto mode (I haven't seen this in the wild)
// 4 -> shutter priority mode (haven't seen this)
// 8 -> aperature prioritry mode (seen this used)
value &= 0xFF;
*enabled = (value==1) ? false : true;
return true;
}
return false;
case CAPPROPID_WHITEBALANCE:
if ([self getData:PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL unit:_processingUnitID length:1 data:&value]) {
value &= 0xFF;
*enabled = (value==1) ? true : false;
UVCDEBUG3("White balance auto mode: {}", *enabled ? "enabled" : "disabled");
return true;
}
return false;
case CAPPROPID_FOCUS:
if ([self getData:CT_FOCUS_AUTO_CONTROL unit:UVC_INPUT_TERMINAL_ID length:1 data:&value]) {
value &= 0xFF;
*enabled = (value==1) ? true : false;
UVCDEBUG3("Focus auto mode: {}", *enabled ? "enabled" : "disabled");
return true;
}
return false;
default:
UVCWARNING("Unsupported auto property ID: {}", propID);
return false;
}
}
- (bool)getPropertyLimits:(uint32_t)propID min:(int32_t*)min max:(int32_t*)max defValue:(int32_t*)defValue status:(CS_Status*)status {
if (_controlInterface == nullptr) {
*status = CS_UVC_STATUS_DEVICE_DISCONNECTED;
return false;
}
bool ok = true;
if (propID < CAPPROPID_LAST) {
uint32_t unit = (propertyInfo[propID].unit == 0) ? UVC_INPUT_TERMINAL_ID : _processingUnitID;
if (![self getMinData:propertyInfo[propID].selector unit:unit length:propertyInfo[propID].length data:min]) {
ok = false;
}
if (![self getMaxData:propertyInfo[propID].selector unit:unit length:propertyInfo[propID].length data:max]) {
ok = false;
}
if (![self getDefault:propertyInfo[propID].selector unit:unit length:propertyInfo[propID].length data:defValue]) {
ok = false;
}
switch(propertyInfo[propID].length) {
case 2:
*min = static_cast<int16_t>(*min);
*max = static_cast<int16_t>(*max);
*defValue = static_cast<int16_t>(*defValue);
break;
case 1:
*min = static_cast<int8_t>(*min);
*max = static_cast<int8_t>(*max);
*defValue = static_cast<int8_t>(*defValue);
break;
default:
break;
}
} else {
UVCWARNING("getPropertyLimits: property ID out of bounds");
ok = false;
}
return ok;
}
- (void)reportCapabilities:(uint32_t)selector unit:(uint32_t)unit {
uint32_t info;
[self getInfo:selector unit:unit data:&info];
if (info & 0x01) {
UVCDEBUG4("GET ");
}
if (info & 0x02) {
UVCDEBUG4("SET ");
}
if (info & 0x04) {
UVCDEBUG4("DISABLED ");
}
if (info & 0x08) {
UVCDEBUG4("AUTO-UPD ");
}
if (info & 0x10) {
UVCDEBUG4("ASYNC ");
}
if (info & 0x20) {
UVCDEBUG4("DISCOMMIT");
}
UVCDEBUG4("");
}
@end

View File

@@ -12,16 +12,16 @@ static wpi::Event& GetInstance() {
}
namespace cs {
void RunMainRunLoop() {
wpi::Event& event = GetInstance();
wpi::WaitForObject(event.GetHandle());
}
int RunMainRunLoopTimeout(double timeoutSeconds) {
int RunMainRunLoopTimeout(double timeout) {
wpi::Event& event = GetInstance();
bool timedOut = false;
bool signaled =
wpi::WaitForObject(event.GetHandle(), timeoutSeconds, &timedOut);
bool signaled = wpi::WaitForObject(event.GetHandle(), timeout, &timedOut);
if (timedOut) {
return 3;
}
@@ -35,4 +35,5 @@ void StopMainRunLoop() {
wpi::Event& event = GetInstance();
event.Set();
}
} // namespace cs

25
datalog/.styleguide Normal file
View File

@@ -0,0 +1,25 @@
cppHeaderFileInclude {
\.h$
}
cppSrcFileInclude {
\.cpp$
}
licenseUpdateExclude {
examples/printlog
}
modifiableFileExclude {
examples/printlog/datalog\.py$
}
repoRootNameOverride {
datalog
}
includeOtherLibs {
^fmt/
^gtest/
^wpi/(?!datalog)
}

107
datalog/BUILD.bazel Normal file
View File

@@ -0,0 +1,107 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_test")
load("@rules_java//java:defs.bzl", "java_binary")
load("@rules_python//python:defs.bzl", "py_binary")
load("//shared/bazel/rules:cc_rules.bzl", "wpilib_cc_library")
load("//shared/bazel/rules:java_rules.bzl", "wpilib_java_junit5_test")
load("//shared/bazel/rules:jni_rules.bzl", "wpilib_jni_cc_library", "wpilib_jni_java_library")
wpilib_cc_library(
name = "datalog.static",
srcs = glob(
["src/main/native/cpp/**"],
exclude = ["src/main/native/cpp/jni/**"],
),
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 = [
"//wpiutil:wpiutil.static",
],
)
wpilib_jni_cc_library(
name = "datalogjni",
srcs = glob(["src/main/native/cpp/jni/**"]),
java_dep = ":datalog-java",
visibility = ["//visibility:public"],
deps = [
":datalog.static",
],
)
wpilib_jni_java_library(
name = "datalog-java",
srcs = glob(["src/main/java/**/*.java"]),
native_libs = [":datalogjni"],
visibility = ["//visibility:public"],
deps = [
"//wpiutil:wpiutil-java",
"@maven//:us_hebi_quickbuf_quickbuf_runtime",
],
)
py_binary(
name = "datalog",
srcs = ["examples/printlog/datalog.py"],
target_compatible_with = select({
"@rules_bzlmodrio_toolchains//constraints/is_systemcore:systemcore": ["@platforms//:incompatible"],
"//conditions:default": [],
}),
)
cc_binary(
name = "printlog",
srcs = ["examples/printlog/printlog.cpp"],
deps = [
":datalog.static",
"//wpiutil:wpiutil.static",
],
)
cc_binary(
name = "writelog",
srcs = ["examples/writelog/writelog.cpp"],
deps = [
":datalog.static",
"//wpiutil:wpiutil.static",
],
)
java_binary(
name = "printlog-java",
srcs = ["src/printlog/java/printlog/PrintLog.java"],
main_class = "printlog.PrintLog",
deps = [
":datalog-java",
],
)
cc_test(
name = "datalog-cpp-test",
size = "small",
srcs = glob(["src/test/native/**/*.cpp"]),
tags = [
"exclusive",
"no-asan",
"no-tsan",
],
deps = [
":datalog.static",
"//thirdparty/googletest:googletest.static",
"//wpiutil:wpiutil-testlib",
],
)
wpilib_java_junit5_test(
name = "datalog-java-test",
srcs = glob(["src/test/java/**/*.java"]),
tags = ["exclusive"],
deps = [
":datalog-java",
"//wpiutil:wpiutil-java",
],
)

108
datalog/CMakeLists.txt Normal file
View File

@@ -0,0 +1,108 @@
project(datalog)
include(CompileWarnings)
file(GLOB datalog_native_src src/main/native/cpp/*.cpp)
file(GLOB datalog_jni_src src/main/native/cpp/jni/DataLogJNI.cpp)
list(REMOVE_ITEM datalog_native_src ${datalog_jni_src})
add_library(datalog ${datalog_native_src})
set_target_properties(datalog PROPERTIES DEBUG_POSTFIX "d")
target_compile_features(datalog PUBLIC cxx_std_20)
if(MSVC)
target_compile_options(
datalog
PUBLIC /permissive- /Zc:preprocessor /Zc:__cplusplus /Zc:throwingNew /MP /bigobj /utf-8
)
target_compile_definitions(datalog PRIVATE -D_CRT_SECURE_NO_WARNINGS)
endif()
wpilib_target_warnings(datalog)
target_include_directories(
datalog
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
)
target_link_libraries(datalog PRIVATE wpiutil)
subdir_list(datalog_examples "${CMAKE_CURRENT_SOURCE_DIR}/examples")
foreach(example ${datalog_examples})
file(GLOB datalog_example_src examples/${example}/*.cpp)
if(datalog_example_src)
add_executable(datalog_${example} ${datalog_example_src})
wpilib_target_warnings(datalog_${example})
target_link_libraries(datalog_${example} datalog wpiutil)
set_property(TARGET datalog_${example} PROPERTY FOLDER "examples")
endif()
endforeach()
# Java bindings
if(WITH_JAVA)
include(UseJava)
set(CMAKE_JNI_TARGET true)
file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
file(GLOB QUICKBUF_JAR ${WPILIB_BINARY_DIR}/wpiutil/thirdparty/quickbuf/*.jar)
add_jar(
datalog_jar
${JAVA_SOURCES}
# INCLUDE_JARS ${JACKSON_JARS} ${QUICKBUF_JAR}
INCLUDE_JARS wpiutil_jar ${QUICKBUF_JAR}
OUTPUT_NAME datalog
OUTPUT_DIR ${WPILIB_BINARY_DIR}/${java_lib_dest}
GENERATE_NATIVE_HEADERS datalog_jni_headers
)
set_property(TARGET datalog_jar PROPERTY FOLDER "java")
install_jar(datalog_jar DESTINATION ${java_lib_dest})
install_jar_exports(TARGETS datalog_jar FILE datalog_jar.cmake DESTINATION share/datalog)
add_library(datalogjni ${datalog_jni_src})
wpilib_target_warnings(datalogjni)
target_link_libraries(datalogjni PUBLIC datalog wpiutil)
set_property(TARGET datalogjni PROPERTY FOLDER "libraries")
target_link_libraries(datalogjni PRIVATE datalog_jni_headers)
add_dependencies(datalogjni datalog_jar)
install(TARGETS datalogjni EXPORT datalogjni)
export(TARGETS datalogjni FILE datalogjni.cmake NAMESPACE datalogjni::)
endif()
if(WITH_JAVA_SOURCE)
include(UseJava)
include(CreateSourceJar)
add_source_jar(
datalog_src_jar
BASE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/src/main/java
OUTPUT_NAME datalog-sources
OUTPUT_DIR ${WPILIB_BINARY_DIR}/${java_lib_dest}
)
set_property(TARGET datalog_src_jar PROPERTY FOLDER "java")
install_jar(datalog_src_jar DESTINATION ${java_lib_dest})
endif()
install(TARGETS datalog EXPORT datalog)
export(TARGETS datalog FILE datalog.cmake NAMESPACE datalog::)
configure_file(datalog-config.cmake.in ${WPILIB_BINARY_DIR}/datalog-config.cmake)
install(FILES ${WPILIB_BINARY_DIR}/datalog-config.cmake DESTINATION share/datalog)
install(EXPORT datalog DESTINATION share/datalog)
if(WITH_TESTS)
file(GLOB_RECURSE datalog_testlib_src src/test/native/include/*.h)
add_library(datalog_testlib INTERFACE ${datalog_test_src})
target_include_directories(datalog_testlib INTERFACE src/test/native/include)
wpilib_add_test(datalog src/test/native/cpp)
target_link_libraries(datalog_test datalog googletest datalog_testlib wpiutil)
if(MSVC)
target_compile_options(datalog_test PRIVATE /utf-8)
endif()
endif()

76
datalog/build.gradle Normal file
View File

@@ -0,0 +1,76 @@
ext {
useJava = true
useCpp = true
baseId = 'datalog'
groupId = 'edu.wpi.first.datalog'
nativeName = 'datalog'
devMain = 'edu.wpi.first.datalog.DevMain'
}
apply from: "${rootDir}/shared/jni/setupBuild.gradle"
nativeUtils.exportsConfigs {
datalog {
}
}
model {
components {
all {
it.sources.each {
it.exportedHeaders {
srcDirs 'src/main/native/include'
}
}
}
}
}
def examplesMap = [:];
file("$projectDir/examples").list(new FilenameFilter() {
@Override
public boolean accept(File current, String name) {
return new File(current, name).isDirectory();
}
}).each {
examplesMap.put(it, [])
}
model {
components {
examplesMap.each { key, value ->
"${key}"(NativeExecutableSpec) {
targetBuildTypes 'debug'
binaries.all {
lib library: 'datalog', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
}
sources {
cpp {
source {
srcDirs 'examples/' + "${key}"
include '**/*.cpp'
}
}
}
}
}
}
}
sourceSets {
printlog
}
task runPrintLog(type: JavaExec) {
classpath = sourceSets.printlog.runtimeClasspath
mainClass = 'printlog.PrintLog'
}
dependencies {
api project(":wpiutil")
printlogImplementation sourceSets.main.output
}

View File

@@ -0,0 +1,5 @@
@FILENAME_DEP_REPLACE@
include(${SELF_DIR}/wpiutil.cmake)
if(@WITH_JAVA@)
include(${SELF_DIR}/wpiutil_jar.cmake)
endif()

View File

@@ -190,3 +190,9 @@ Each entry's data type is an arbitrary string. The following data types are stan
=== Metadata
Each entry has an associated metadata string. If not blank, the metadata should be <<JSON,JSON>>, but may be arbitrary text. Metadata is intended to convey additional information about the entry beyond what the type conveys--for example the source of the data.
[[additional-resources]]
== Additional Resources
A https://kaitai.io/[Kaitai Struct] definition for the data log format is included in link:./wpilog.ksy[wpilog.ksy].
Kaitai Struct is a declarative language used to describe various binary data structures.

130
datalog/doc/wpilog.ksy Normal file
View File

@@ -0,0 +1,130 @@
meta:
id: wpilog
title: WPILib Data Log
file-extension: wpilog
ks-version: "0.10"
encoding: UTF-8
endian: le
bit-endian: le
seq:
- id: header
type: header
- id: records
type: record
repeat: eos
types:
version_number:
seq:
- id: minor
type: u1
- id: major
type: u1
header:
seq:
- id: magic
contents: WPILOG
- id: version
type: version_number
- id: extra_header_length
type: u4
- id: extra_header
size: extra_header_length
type: str
record:
seq:
- id: header_length
type: record_header_length
- id: entry_id
type:
switch-on: header_length.len_entry_id
cases:
0: u1
1: u2
2: b24
3: u4
- id: len_payload
type:
switch-on: header_length.len_payload_size
cases:
0: u1
1: u2
2: b24
3: u4
- id: timestamp
type:
switch-on: header_length.len_timestamp
cases:
0: u1
1: u2
2: b24
3: u4
4: b40
5: b48
6: b56
7: u8
- id: payload
size: len_payload
type:
switch-on: entry_id
cases:
# ID 0 is reserved for control records
0: control_record_payload
record_header_length:
seq:
- id: len_entry_id
type: b2
- id: len_payload_size
type: b2
- id: len_timestamp
type: b3
- id: spare_bit
type: b1
control_record_payload:
seq:
- id: type
type: u1
enum: control_record_type
- id: data
type:
switch-on: type
cases:
"control_record_type::start": control_record_start_data
"control_record_type::finish": control_record_finish_data
"control_record_type::set_metadata": control_record_set_metadata_data
control_record_start_data:
seq:
- id: entry_id
type: u4
- id: len_entry_name
type: u4
- id: entry_name
size: len_entry_name
type: str
- id: len_entry_type
type: u4
- id: entry_type
size: len_entry_type
type: str
- id: len_entry_metadata
type: u4
- id: entry_metadata
size: len_entry_metadata
type: str
control_record_finish_data:
seq:
- id: entry_id
type: u4
control_record_set_metadata_data:
seq:
- id: entry_id
type: u4
- id: len_entry_metadata
type: u4
- id: entry_metadata
size: len_entry_metadata
type: str
enums:
control_record_type:
0: start
1: finish
2: set_metadata

View File

@@ -9,11 +9,11 @@
#include <fmt/chrono.h>
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <wpi/DenseMap.h>
#include <wpi/MemoryBuffer.h>
#include <wpi/print.h>
#include "wpi/DataLogReader.h"
#include "wpi/DenseMap.h"
#include "wpi/MemoryBuffer.h"
#include "wpi/print.h"
#include "wpi/datalog/DataLogReader.h"
int main(int argc, const char** argv) {
if (argc != 2) {

View File

@@ -8,8 +8,9 @@
#include <utility>
#include <vector>
#include "wpi/DataLogBackgroundWriter.h"
#include "wpi/print.h"
#include <wpi/print.h>
#include "wpi/datalog/DataLogBackgroundWriter.h"
int main(int argc, char** argv) {
using std::chrono::duration_cast;

View File

@@ -2,10 +2,11 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "frc/shuffleboard/LayoutType.h"
package edu.wpi.first.datalog;
using namespace frc;
public final class DevMain {
/** Main entry point. */
public static void main(String[] args) {}
std::string_view LayoutType::GetLayoutName() const {
return m_layoutName;
private DevMain() {}
}

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
import java.util.Arrays;

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
/** Log boolean values. */
public class BooleanLogEntry extends DataLogEntry {

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
import edu.wpi.first.util.WPIUtilJNI;
import edu.wpi.first.util.protobuf.Protobuf;
@@ -451,10 +451,10 @@ public class DataLog implements AutoCloseable {
if (!seen.add(typeString)) {
throw new UnsupportedOperationException(typeString + ": circular reference with " + seen);
}
addSchema(typeString, "structschema", struct.getSchema(), timestamp);
for (Struct<?> inner : struct.getNested()) {
addSchemaImpl(inner, timestamp, seen);
}
addSchema(typeString, "structschema", struct.getSchema(), timestamp);
seen.remove(typeString);
}

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
/**
* A data log background writer that periodically flushes the data log on a background thread. The

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
/** Log entry base class. */
public class DataLogEntry {

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
import java.util.Iterator;
import java.util.NoSuchElementException;

View File

@@ -2,18 +2,72 @@
// 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.util.datalog;
package edu.wpi.first.datalog;
import edu.wpi.first.util.WPIUtilJNI;
import edu.wpi.first.util.RuntimeLoader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* DataLog wpiutil JNI Functions.
* DataLog JNI Functions.
*
* @see "wpiutil/DataLog.h"
* @see "datalog/DataLog.h"
*/
public class DataLogJNI extends WPIUtilJNI {
public class DataLogJNI {
static boolean libraryLoaded = false;
/** Sets whether JNI should be loaded in the static block. */
public static class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
/**
* Returns true if the JNI should be loaded in the static block.
*
* @return True if the JNI should be loaded in the static block.
*/
public static boolean getExtractOnStaticLoad() {
return extractOnStaticLoad.get();
}
/**
* Sets whether the JNI should be loaded in the static block.
*
* @param load Whether the JNI should be loaded in the static block.
*/
public static void setExtractOnStaticLoad(boolean load) {
extractOnStaticLoad.set(load);
}
/** Utility class. */
private Helper() {}
}
static {
if (Helper.getExtractOnStaticLoad()) {
try {
RuntimeLoader.loadLibrary("datalogjni");
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
libraryLoaded = true;
}
}
/**
* Force load the library.
*
* @throws IOException if the library failed to load
*/
public static synchronized void forceLoad() throws IOException {
if (libraryLoaded) {
return;
}
RuntimeLoader.loadLibrary("datalogjni");
libraryLoaded = true;
}
/**
* Create a new Data Log background writer. The log will be initially created with a temporary
* filename.
@@ -298,6 +352,24 @@ public class DataLogJNI extends WPIUtilJNI {
*/
static native void appendStringArray(long impl, int entry, String[] value, long timestamp);
/**
* Create a native FileLogger. When the specified file is modified, appended data will be appended
* to the specified data log.
*
* @param file path to the file
* @param log data log implementation handle
* @param key log key to append data to
* @return The FileLogger handle.
*/
public static native long createFileLogger(String file, long log, String key);
/**
* Free a native FileLogger. This causes the FileLogger to stop appending data to the log.
*
* @param fileTail The FileLogger handle.
*/
public static native void freeFileLogger(long fileTail);
/** Utility class. */
private DataLogJNI() {}
}

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
import java.io.IOException;
import java.io.RandomAccessFile;

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
import java.io.IOException;
import java.io.OutputStream;

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
import java.util.Arrays;

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
/** Log double values. */
public class DoubleLogEntry extends DataLogEntry {

View File

@@ -2,9 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util;
import edu.wpi.first.util.datalog.DataLog;
package edu.wpi.first.datalog;
/**
* A class version of `tail -f`, otherwise known as `tail -f` at home. Watches a file and puts the
@@ -22,11 +20,11 @@ public class FileLogger implements AutoCloseable {
* @param key The log key to append data to.
*/
public FileLogger(String file, DataLog log, String key) {
m_impl = WPIUtilJNI.createFileLogger(file, log.getImpl(), key);
m_impl = DataLogJNI.createFileLogger(file, log.getImpl(), key);
}
@Override
public void close() {
WPIUtilJNI.freeFileLogger(m_impl);
DataLogJNI.freeFileLogger(m_impl);
}
}

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
import java.util.Arrays;

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
/** Log float values. */
public class FloatLogEntry extends DataLogEntry {

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
import java.util.Arrays;

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
/** Log integer values. */
public class IntegerLogEntry extends DataLogEntry {

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
package edu.wpi.first.datalog;
import edu.wpi.first.util.protobuf.Protobuf;
import edu.wpi.first.util.protobuf.ProtobufBuffer;

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