Compare commits

...

355 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
Jack Cammarata
1efaaefd78 [wpimath] Fix UnscentedKalmanFilter and improve math docs (#7850)
Throughout the code the state sqrt covariance S and innovation covariance Sy are maintained as upper triangular cholesky factors of those covariance matrices. The original paper defines P=S*S', so S should be lower triangular. The functions in the paper reflect this. In the code implementation, the sqrt covariance matrices are upper triangular, but the algorithm expects them to be lower triangular.

This bug was likely missed because the incorrect version of the filter is able to converge for some systems where all the states are observed, and the test case is set up such that all states are observed.

To fix the bug, a couple things needed to be changed:
all instances of rankUpdate() needed to be changed to use the lower triangular cholesky factor,
In the unscented transform, when S is found via QR decomposition, we need to take the transpose because R is upper triangular,
P() and SetP() functions need to be modified to be P=S*S' instead of P=S'*S, and P.llt().matrixL() instead of P.llt().matrixU() respectively.

Each part of the algorithm has also had the comments changed to clarify exactly which equation from the paper it implements.

Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2025-03-14 10:05:15 -07:00
Tyler Veness
71b6e8ec58 [wpiutil] Avoid including execinfo.h for Emscripten target (#7854) 2025-03-06 23:15:22 -08:00
Tyler Veness
1a835fec01 [wpimath] Fix singularities in Ellipse2d::Nearest() (#7851)
The problem was ill-conditioned if either semiaxis had zero length.
2025-03-04 18:52:17 -08:00
Tyler Veness
d3cc185382 [upstream_utils] Upgrade to fmt 11.1.4 (#7852) 2025-03-04 18:50:56 -08:00
crueter
0ad595c33c [wpimath] Add Debouncer type and time setters (#7839)
Signed-off-by: swurl <swurl@swurl.xyz>
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2025-03-03 17:21:38 -07:00
Tyler Veness
7cb29ce70b [datalog] Fix sorting of related header (#7832) 2025-03-02 10:47:48 -08:00
Thad House
93bf6c70ba [build] Remove sources from ceres-cpp (#7844) 2025-03-02 10:46:50 -08:00
arbessette
a7ae22d764 [docs] Update code of conduct (#7833) 2025-02-27 11:02:13 -08:00
Tyler Veness
822457d45b [wpimath] Fix feedforward returning NaN when kᵥ = 0 (#7790) 2025-02-25 19:07:51 -08:00
Thad House
baa20fa239 [hal, wpilib] Rewrite CAN APIs (#7798) 2025-02-25 19:07:01 -08:00
Adrien Bourdeaux
75321f1d84 [wpimath] Add Translation2d/Translation3d slew rate limiter (#7806)
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
Co-authored-by: Joseph Eng <91924258+KangarooKoala@users.noreply.github.com>
2025-02-25 19:06:00 -08:00
Tyler Veness
cd6fee7fea [sysid] Refactor feedback analysis (#7827) 2025-02-25 19:05:05 -08:00
Tyler Veness
517344fe80 [wpimath] Fix another infinite loop in ArmFeedforward (#7823) 2025-02-25 19:04:31 -08:00
Rain Heuer
b0e588fd49 [glass] Update Field2D default field to 2025 (#7820) 2025-02-25 19:04:16 -08:00
Tyler Veness
0f0e93722e [ci] Upgrade to Clang 17 sanitizers (#7819) 2025-02-25 19:01:41 -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
sciencewhiz
f9307de04c [docs] Document that /format is disabled (#7810)
Add instructions for manual workarounds
2025-02-20 20:13:09 -08:00
sciencewhiz
15dcdebe21 [docs] Update contributing so breaking changes go to 2027 (#7809) 2025-02-20 20:12:48 -08:00
Peter Johnson
98f933eca5 Merge branch 'main' into 2027 2025-02-20 00:26:23 -08:00
Tyler Veness
0c3c2c1fda [sysid] Remove extra period from exception messages (#7805) 2025-02-19 21:08:39 -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
HarryXChen
c898853b4d [wpilib] LinearSystemSim.setState: calculate new output state (#7799)
Establishes the invariant that the state and measurement always match up, even immediately after construction.
2025-02-18 22:49:28 -08:00
Sam Carlberg
bd2211119f [epilogue] Make nonloggable type warnings configurable (#7792)
Now a flag at the class level controls whether warning messages are printed.

Defaults to false (no warning messages).
2025-02-18 21:48:51 -08:00
Tyler Veness
13626063dc [wpimath] Fix units typo (#7789) 2025-02-13 23:22:44 -08:00
Peter Johnson
7e6077c546 [wpimath] Fix up order and docs for Feedforward gain setters (#7788) 2025-02-13 23:20:08 -08:00
Jade
4d126b158c [wpimath] Add setters to Feedforward gains (#7784)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-02-13 21:09:28 -08:00
Peter Johnson
e22f76ce73 [build] Bump native-utils to 2025.9.1 (#7783) 2025-02-13 20:23:07 -08:00
Jonah Bonner
e648b9c86d [wpinet] Serve index HTML file from WebServer if available (#7780) 2025-02-13 18:10:02 -08:00
Kevin-OConnor
23658a8c62 [apriltag] Split 2025 AprilTag Maps (#7781)
Splits maps for welded vs AndyMark field perimeters. More info about why and what fields are at what events will be in TU12.
2025-02-13 17:53:11 -08:00
Tyler Veness
155b3d45e7 [wpiutil] Remove broken StackWalker library (#7777)
A Discord user reported that StackWalker gives blank stacktraces.

MSVC's C++23 support is unstable, so we can't use std::stacktrace yet.
In the meantime, we can just return an empty string and remove the
unmaintained StackWalker library and its hacky upstream_utils script.
2025-02-12 22:54:41 -08:00
Austin Schuh
d62ab12855 [bazel] Add missing -ldl when building libuv in bazel (#7775)
I was getting:
external/arm_frc_linux_gnueabi_repo/bin/../arm-nilrt-linux-gnueabi/sysroot/usr/lib/gcc/arm-nilrt-linux-gnueabi/12/../../../../../../../arm-nilrt-linux-gnueabi/bin/ld: bazel-out/k8-opt--roborio/bin/external/com_github_wpilibsuite_allwpilib/wpinet/libwpinet.static.a(fs.o): undefined reference to symbol 'dlsym@@GLIBC_2.4'
external/arm_frc_linux_gnueabi_repo/bin/../arm-nilrt-linux-gnueabi/sysroot/usr/lib/gcc/arm-nilrt-linux-gnueabi/12/../../../../../../../arm-nilrt-linux-gnueabi/bin/ld: external/arm_frc_linux_gnueabi_repo/bin/../arm-nilrt-linux-gnueabi/sysroot/lib/libdl.so.2: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status

This fixes that error for me.

Signed-off-by: Austin Schuh <austin.linux@gmail.com>
2025-02-12 16:17:11 -07:00
Dustin Spicuzza
1921d7b79a [wpilibc] Remove Alert magic static with map lookup (#7712)
The magic static adds yet another thing that needs to be reset if you
want to run a unit test with a completely reset wpilib state. Use the
existing Sendable static map to store the data instead.

This leaks memory if ResetSmartDashboardInstance is called.
2025-02-11 22:05:22 -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
Gold856
9dbb0c8c3d [ci] Turn on sync labels for the labeler (#7770)
This means PRs that had labels for certain changes will have those labels removed if those changes are removed.
2025-02-09 23:04:36 -08:00
Michael Fisher
b2584c6bdf [cmake] Use binary output dir for generated field images code (#7772) 2025-02-09 23:03:15 -08:00
Peter Johnson
53df127535 [cmake] Suppress enum warning on all clang, not just Apple (#7771) 2025-02-09 23:01:51 -08:00
Peter Johnson
d2611d4ad5 [hal] SPI: Remove byte limit on size in Java API (#7774)
The underlying Linux spidev supports up to page size length.
2025-02-09 23:01:01 -08:00
Peter Johnson
764ada9b66 [hal] Change usage reporting to string-based (#7763) 2025-02-07 13:37:23 -07:00
Ryan Blue
b60b2b64bd [hal, wpilib] AddressableLED: add support for other color orders (#7102)
Many LED strips use different color order (GRB in particular is common).

This makes the change at the HAL level. This solves 2 problems; first, no code needs to change in the high level drivers, which was challenging for C++, and second, simulation will behave properly as no conversion is needed. The HAL will accept an array of data objects in the same order no matter what the selected output order is, and will convert before sending it to the FPGA for output.

To accomplish this, NEON bulk load/interleave instructions are utilized. The low level implementation (load, store, and alignment functions) come from the Simd Library. The high level implementations are inspired by the image conversion functions in the simd library, but have diverged significantly.

Much of the implementation uses templates and inlined functions rather than runtime parameters; This is a trade off between the size of the generated code and the amount of function calls done at runtime. Currently, the entire conversion operation is inlined.
2025-02-07 13:36:41 -07:00
Ryan Blue
a0976a1fd9 [build] Update developerRobot JRE (#7764) 2025-02-07 11:24:30 -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
b3b515e1dd [sysid] Error on missing tests in loaded DataLog (#7747)
* add exception

* detect missing tests

* throw in analyzer

* define tests setter

* change to std::map

* alignas fail

* make missingTests parameter const&

* const& impl

* use set and naive comparison

* use default comparison

* add <utility>

* Revert "alignas fail"

This reverts commit 5e97940f34.

* indent validtests

* format
2025-02-04 18:12:00 -05:00
Joseph Eng
296986397b [wpimath] Document drift from desaturating discretized chassis speeds (NFC) (#7741) 2025-02-03 11:46:18 -07: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
Sam Carlberg
18321e5551 [epilogue] Fix epilogue with package-info files (#7749) 2025-01-30 13:34:51 -07:00
Tyler Veness
b31fd17d05 [wpimath] Fix infinite loop in ArmFeedforward::Calculate(xₖ, vₖ, vₖ₊₁) (#7745)
Small values of kₐ make the iterative solver ill-conditioned. This
change reverts to the constant-acceleration feedforward in that case. It
gives _very_ bad results (hence why we added the iterative solver in the
first place), but it's better than hanging.

```
TEST(ArmFeedforwardTest, CalculateIllConditioned) {
  constexpr auto Ks = 0.5_V;
  constexpr auto Kv = 20_V / 1_rad_per_s;
  constexpr auto Ka = 1e-2_V / 1_rad_per_s_sq;
  constexpr auto Kg = 0_V;
  frc::ArmFeedforward armFF{Ks, Kg, Kv, Ka};

  // Calculate(currentAngle, currentVelocity, nextAngle, dt)
  CalculateAndSimulate(armFF, 0_rad, 0_rad_per_s, 2_rad_per_s, 20_ms);
}
```
This produces 1 V and doesn't accelerate the system at all. Using
nextVelocity instead of currentVelocity in the feedforward outputs 41 V
and still only accelerates to 0.4 rad/s of the requested 2 rad/s.

I picked the kₐ cutoff by increasing kₐ until the iterative solver
started converging.

Fixes #7743.
2025-01-30 13:33:39 -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
Gold856
b44a80c07a [build] cmake: Install wpimath nanopb headers (#7731) 2025-01-24 23:26:09 -08:00
Gold856
25d11524e8 [ci] Re-enable Artifactory cleaner cron job (#7721)
The query now targets the local repo for extra safety.
2025-01-23 21:46:13 -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
Peter Johnson
d86a2ec83b [ci] Fix labeler indentation (#7716) 2025-01-21 12:51:48 -07:00
sciencewhiz
0690d3d832 [ci] Update labeler for wpical and usage reporting (#7710) 2025-01-20 09:14:27 -07:00
Ryan Blue
304b98c0c9 [wpilibc] Alert: Fix first alert in group not publishing data (#7711) 2025-01-20 09:10:03 -07:00
Joseph Eng
72541c10e6 [wpilib, commands] Improve HID direction documentation (NFC) (#7672) 2025-01-19 20:34:07 -08:00
sciencewhiz
00445f4f27 [hal] Add Kitbot framework usage reporting (#7709)
Used in FIRST's kitbot code
2025-01-18 15:02:41 -08:00
Matthew Wozniak
8fc3767b30 [wpimath] Fix macro name typo (#7707) 2025-01-17 22:19:21 -08:00
Peter Johnson
5ab0409c15 [wpilib] ADIS164xx: report product ID on mismatch (#7706) 2025-01-17 18:14:20 -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
Peter Johnson
4caa16e254 [wpilibj] ADIS16470: Allow product ID of 16470 (#7704)
C++ allows either 16982 or 16470, do the same for Java.
2025-01-17 13:53:20 -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
Sam Carlberg
e52f400687 [wpiunits] Add Measure.per overloads for all known unit types (#7699)
Instead of only providing per(TimeUnit)

Useful for making conversion factors easier, eg `Inches.of(10).per(Rotation)` vs `Inches.of(10).per(Rotation.one())`

Update VelocityUnit.one() and VelocityUnit.zero() to return Velocity objects instead of generic Measure<? extends VelocityUnit<D>>; VelocityUnit is final, so the wildcard generic is unnecessary, and this makes the generated `per` functions possible for this type
2025-01-16 23:24:11 -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
PJ Reiniger
a9f3fc6b2c [bazel] Update toolchain to support systemcore (#7689) 2025-01-16 10:52:43 -07: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
David Racovan
a14545102f [wpimath] DifferentialDriveWheelPositions: tag as Proto/StructSerializable (#7622) 2025-01-13 14:52:40 -07:00
Tyler Veness
25e6549398 [wpimath] Fix various constexpr support bugs (#7676) 2025-01-13 14:44:55 -07:00
ハイドラント
cd92b07321 [wpimath] Add Pose2d and Pose3d rotateAround() (#7659) 2025-01-13 12:55:26 -07:00
Jason Daming
fc9e413ce1 [hal, wpilib] Add note about support for WS2815 (#7664) 2025-01-13 12:26:54 -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
Tyler Veness
007526089e [wpimath] Fix LinearSystemId return type and docs (#7675)
Fixes #7674.
2025-01-13 12:22:53 -07:00
Thad House
666d1638ce [hal] Digital IO SystemCore implementation (#7621) 2025-01-12 16:58:46 -08:00
Sam Carlberg
c5f7a2b4ac [epilogue] Fix lazy logging of mutable arrays (#7665) 2025-01-11 10:25:47 -08:00
Jade
638d265b33 [commands] Add a warning to schedule docs (NFC) (#7073)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-01-09 23:31:40 -08:00
Elliot Scher
6125227836 [wpical] Add JSON Combiner (#7640)
This new feature allows users to combine multiple Apriltag layouts. This can be useful for fields where the apriltags are split into two or more sections: (red/blue side, grouped together by task, etc.)
2025-01-09 23:30:17 -08:00
HarryXChen
e37c35746a [wpimath] Implement Translation3d.RotateAround (#7661) 2025-01-09 20:31:34 -08:00
oh-yes-0-fps
995bc98ccf [epilogue] Revert "Add a measure's symbol to its name when logged by Epilogue (#7535)" (#7652)
This reverts commit 469bb3290d.

The approach used has issues due to the fact unit symbols often have a literal / in them,
which causes issues with NT topic visualization.

A better approach would be to use topic metadata.
2025-01-07 12:35:10 -07:00
Matt
2de03c9601 [cscore] Use frame time in Linux UsbCameraImpl (#7609) 2025-01-07 09:33:20 -07:00
Ryan Heuer
8e459a4f2a [glass] Field2d: Fix custom image padding to maintain aspect ratio (#7648) 2025-01-06 21:16:14 -08:00
Peter Johnson
58d7c07343 [fieldImages] Use rendered image (#7650)
Source: https://www.chiefdelphi.com/t/4k-field-image-2025-reefscape/478797
2025-01-06 21:15:39 -08:00
Peter Johnson
9b08f0244c [wpiutil] SignalObject: Fix move operator= (#7649) 2025-01-06 20:11:34 -08:00
sciencewhiz
7032de3d5d [glass] Field2D: Change field picker to show JSONs first (#7643)
Too many people don't realize that glass/simgui field2d can load
pathweaver JSON field files since it's hidden.
2025-01-05 22:36:14 -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
Kevin-OConnor
159e18ce05 [fieldImages] Flip 2025 Field Image (#7638)
Original image was flipped when it should have been rotated.
2025-01-04 11:37:44 -08:00
Peter Johnson
257d0e0824 [fieldImages] Add 2025 Reefscape to Fields, make default (#7635) 2025-01-04 11:14:27 -08:00
Kevin-OConnor
b65f159c3f Add 2025 field and apriltags (#7634)
Column in Field Drawings is labeled X-Rotation, but I believe it should be Y-Rotation so have reflected that here. We'll fix in a TU if this is correct.
2025-01-04 10:14:34 -08:00
Tyler Veness
11a0c36737 [wpimath] Make Rotation2d member initialization order match declaration order (#7632) 2025-01-03 22:44:17 -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
Wispy
17a03514ba [glass, simgui] Fix minimum widths of windows (#7604) 2025-01-01 15:20:35 -08:00
Jade
9ebc4b32ae [commands] Undeprecate deferredProxy (#7417)
This changes the way deferred proxy is implemented to not use the
deprecated ProxyCommand constructor.

This function serves a good purpose that should be kept IMO. The
constructor was confusing but this is just good syntactic sugar over
`defer(() -> supplier.get().asProxy())`.

Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-01-01 15:11:39 -08:00
Elliot Scher
ce60bd5035 [wpical] Add support for new Apriltags (ID 16-22) (#7619) 2025-01-01 14:53:58 -08:00
Jade
468a3c6d95 [apriltag] Add kDefaultField to C++ (#7618)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-01-01 14:34:51 -08:00
Peter Johnson
4350ea769c [build] Bump ni-libraries to 2025.2.0 (#7617) 2024-12-31 20:50:50 -08:00
sciencewhiz
83397392f4 [hal] Update Usage Reporting to match 2025v2 image numbers (#7616) 2024-12-31 20:24:26 -08:00
Tyler Veness
786d22049b [wpilibc] Rename DCMotorSim getters (#7614) 2024-12-31 16:48:45 -08:00
Dustin Spicuzza
86137c49f5 [wpilibc] DCMotorSim: Add setAngle/setAngularVelocity (parity with Java) (#7613) 2024-12-31 14:30:16 -08:00
Tyler Veness
4edf52d3b6 [wpilibc] Clean up Joystick::GetDirection() (#7612) 2024-12-31 13:31:05 -08:00
Gold856
a41fb460a9 Update ThirdPartyNotices.txt (#7608) 2024-12-30 20:16:10 -06:00
sciencewhiz
d4985b8ba0 [ci] Build RobotPy in tools workflow (#6800) 2024-12-29 23:47:45 -06:00
sciencewhiz
1538370034 Update license year to 2025 (#7607) 2024-12-29 23:46:38 -06:00
Peter Johnson
a931a6554f Merge branch 'main' into 2027 2024-12-29 18:22:39 -08:00
Carl Hauser
eef1bf33de [wpilib] Fix SmartDashboard.setDefault* docs (NFC) (#6490)
Fix incorrect comments related to NT SetDefault* methods across multiple components
2024-12-29 18:41:29 -06:00
ハイドラント
78b6d61e88 [commands] Use factories and decorators in Command tests (#7006) 2024-12-29 10:45:17 -06:00
David Vo
e7dd5dca82 [wpilibj] TimedRobot: Squash ErrorProne warnings (#7605) 2024-12-28 21:01:43 -08:00
Matt
a27df8ec24 [cscore] Sink: add ability to get most recent frame instead of waiting (#7572)
This allows more control over frame dropping.
2024-12-28 20:44:48 -08:00
Elliot Scher
85507a6c65 [wpical] Add WPIcal: Field Calibration Tool (#6915)
Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>
Co-authored-by: Jade <spacey-sooty@proton.me>
Co-authored-by: Matthew Morley <matthew.morley.ca@gmail.com>
2024-12-28 20:24:32 -08:00
Peter Johnson
b74f84f876 [upstream_utils] Add imgui_demo.cpp (#7602)
This has useful debugging functions; it was left out of the change
from the separate thirdparty repo.
2024-12-28 19:33:55 -08:00
Joseph Eng
54e9c76e03 [wpilibc] Fully qualify names in error macros (#7601) 2024-12-28 16:59:19 -06:00
sciencewhiz
203487a6aa [wpimath] improve LTVUnicycleController docs (NFC) (#7599)
Document the states and inputs so it isn't necessary to look at the code
Fix max velocity throws doc
2024-12-28 16:03:47 -06:00
Tyler Veness
57344ef3b2 [wpimath] Use ct_matrix instead of Eigen/LU for determinant in headers (#7600)
This caught a bug in ct_matrix's 3x3 determinant.
2024-12-28 16:03:29 -06:00
Peter Johnson
3232630a38 Merge branch 'main' into 2027 2024-12-27 19:40:43 -08:00
sciencewhiz
46d401553e [wpilib] Add Koors40 motor controller (#7469) 2024-12-27 15:04:43 -06:00
Tyler Veness
df244cd198 [wpimath] Clean up arm and elevator feedforward APIs (#7595) 2024-12-27 08:12:14 -06:00
Tyler Veness
9e63dcfb16 [ci] Install wpiformat into venv (#7596) 2024-12-26 21:59:48 -06:00
Gold856
934bf7bf05 [build] CMake: Make NO_WERROR also work on MSVC (#7594) 2024-12-26 21:33:20 -06:00
Peter Johnson
2a757eaeb5 Merge branch 'main' into 2027 2024-12-26 18:55:43 -08:00
Nicholas Armstrong
fe49cbe429 [wpimath] Remove Units class from Elevator and Arm Feedforwards (#7570) 2024-12-26 20:21:19 -06:00
Tyler Veness
0470e51569 [upstream_utils] Upgrade to fmt 11.1.0 (#7593)
Usage of FMT_STRING() was removed since it caused compilation failures,
and https://fmt.dev/11.1/api/#compile-time-checks says it's no longer
necessary for compile-time format strings.

Fixes #7592.
2024-12-26 19:14:02 -06:00
Tyler Veness
72fdca3507 [examples] Fix C-style cast warning from cpplint (#7591) 2024-12-25 23:33:41 -06:00
sciencewhiz
dee50bf500 [ci] Validate gradle wrapper inline (#7582)
Updates to v4 of the gradle wrapper validation action
2024-12-24 17:42:07 -08:00
Tyler Veness
939a9ceee1 [upstream_utils] Upgrade to LLVM 19.1.6 (#7101) 2024-12-24 17:40:31 -08:00
Gold856
b670a59b5b [build] Fix imgui libraries not being published (#7575) 2024-12-23 19:05:06 -08:00
Brendan Raykoff
9b12ddb595 [upstream_utils] Patch protobuf to remove deprecated ATOMIC_VAR_INIT (#7585) 2024-12-23 16:14:15 -06:00
Peter Johnson
76625fa0f9 [build] cmake: Build wpiutil dev executable (#7578) 2024-12-22 15:03:33 -08:00
Peter Johnson
bb130b67b8 [wpiutil] DataLogWriter: Don't crash on file open error (#7579) 2024-12-22 15:03:12 -08:00
Wispy
469bb3290d [epilogue] Add a measure's symbol to its name when logged by Epilogue (#7535) 2024-12-22 13:35:31 -08:00
Tyler Veness
02a0adc653 [wpimath] Add Rotation3d rotation vector getter (#7564)
The code churn in Java is just making the function order consistent
between languages.
2024-12-22 13:34:51 -08:00
Ryan Blue
0c99073b94 Use std::bit_cast (#7567)
Backport #7492

Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2024-12-22 13:34:16 -08:00
Tyler Veness
d631fa8e4b [wpimath] Fix algorithm link (NFC) (#7569)
Fixes #7568.
2024-12-22 13:33:07 -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
Tyler Veness
19d385d149 [ci] Upgrade to wpiformat 2024.51 (#7573) 2024-12-22 13:44:40 -06:00
Ryan Blue
d0cc7e4eca [ci] Disable cleaner cron job (#7574) 2024-12-21 19:08:48 -08:00
Kaya
807dffed35 [commands] Fix C++ iterator invalidation bug (#7554)
Co-authored-by: Joseph Eng <91924258+KangarooKoala@users.noreply.github.com>
Co-authored-by: Ryan Blue <13878527+rzblue@users.noreply.github.com>
2024-12-20 00:32:24 -06:00
Peter Johnson
529bab6ca1 Merge branch 'main' into 2027 2024-12-19 20:40:37 -08:00
Ryan Blue
f46c81cfe3 [ci] Delete comment command (#7566) 2024-12-19 20:53:17 -06:00
PJ Reiniger
9ccd73108b [bazel] MVP for building wpilibc + commands framework (#7231) 2024-12-18 22:00:40 -08:00
Gold856
38d8929f48 [ci] Move pregen steps into a composite action (#7474)
This ensures that complete uniformity in how the generation scripts are run. All dependencies and scripts are set up in the exact same way, each time. The old pregen_all script has been removed and moved into the composite action to ensure failed scripts will always fail the job.
2024-12-18 21:59:47 -08:00
Jan-Felix Abellera
cc73236a06 [hal] Add CAN device type for servo controllers (#7556) 2024-12-18 21:57:34 -08:00
Jade
f8720a628c [commands] Fix proxy command deprecation docs (#7396)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>
2024-12-18 21:57:11 -08:00
Jade
156bd71fef [developerRobot] Workaround Eclipse annotation processor issues (#7537)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2024-12-18 09:46:31 -07:00
Ryan Blue
6e44187ff6 [build] cmake: Add wpilibNewCommands and apriltag to developerRobot (#7557) 2024-12-18 09:35:23 -07:00
Jan-Felix Abellera
e2cbdf9718 [hal] Add usage reporting for REV Servo Hub (#7555) 2024-12-18 09:34:45 -07:00
Jade
66cce1835c [ci] Add Buildifier to /format comment command (#7480)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2024-12-18 09:31:02 -07:00
sciencewhiz
6fe5da7289 [examples] Fix example json for SimpleDifferentialDriveSimulation (NFC) (#7561)
Uses LTVUnicycleController now instead of RAMSETE.
2024-12-18 09:20:31 -07:00
Tyler Veness
03f0fc4dea [wpimath] Use immutable member functions in ChassisSpeeds (#7545) 2024-12-15 16:09:34 -08:00
Peter Johnson
80c391e182 [wpinet] WebServer: Unescape URI (#7552)
Also provide Content-Disposition filename header in response.

This fixes e.g. filenames with spaces in them.
2024-12-15 12:28:08 -08:00
Joseph Eng
70f36cce7e [commands] Extract common trigger binding logic (#7550)
This makes the logic clearer in the actual binding methods and will hopefully make it less annoying to make changes such as allowing control over initial edges.

Also changes Java to use previous and current to match C++.
2024-12-14 23:13:41 -08:00
Peter Johnson
564c1f2de2 [wpinet] WebServer: Fix Windows (#7551)
The order of evaluation of parameters is not defined; on Windows,
the std::move was executed before the GetBuffer().
2024-12-14 23:07:48 -08:00
Peter Johnson
a1b642a402 [wpinet] Add simple web server (#7527)
Also add EscapeHTML to HttpUtil.
2024-12-14 11:51:21 -08:00
Falon
f9b3efb712 [wpiunits] Refactor MomentOfInertiaUnit constructor to use MomentOfInertiaUnit instead of a Per<> (#7546) 2024-12-14 11:26:26 -08:00
Thad House
782459dff4 [wpiutil] Make runtime loader exception message slightly better (#7536) 2024-12-14 11:25:27 -08:00
Peter Johnson
945d416d07 Merge branch 'main' into 2027 2024-12-14 10:45:32 -08:00
Peter Johnson
4bca79b9af [wpimath] Revert "ChassisSpeeds fromRelative and discretize methods made instance methods (#7115)" (#7549)
This reverts commit a3b12b3bd9.
2024-12-14 10:42:49 -08:00
Daniel Chen
68285dae77 [commands] Add withDeadline modifier (#7299)
Co-authored-by: Ryan Blue <ryanzblue@gmail.com>
2024-12-13 18:30:02 -07:00
Daniel Chen
5e3dba672a [wpiutil] Record/Enum struct generation fix (#7538)
ProceduralStructGenerator's genRecord and genEnum were package-private, and only extractClassStruct was made public.
However, this package private visibility rendered them unable to be used by the rest of wpilib(and advanced users).

Here, ProceduralStructGenerator is split into 2 classes: StructGenerator(which generates structs) and StructFetcher(the new namespace for extractClassStruct). In addition, genRecord and genEnum have been made public methods.
2024-12-13 12:25:38 -07:00
Ryan Blue
e943424609 [build] Update shadow plugin (#7540) 2024-12-12 19:19:14 -08:00
Joseph Eng
4225b732fd Remove unnecessary boxing (#7539)
* Remove unnecessary boxing
Also remove unnecessary warning suppression

* Use more idiomatic functional interfaces in NumericalIntegration
2024-12-12 19:18:40 -08:00
Carl Hauser
39d05ebe7c [rtns] Capture exitCode from ssh_channel_get_exit_status (#7541) 2024-12-12 19:18:02 -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
Wispy
cc41a0ed24 [wpilib] Replace MecanumDriveMotorVoltages with a functional interface (#6760) 2024-12-08 17:05:06 -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
d5edb4060d [upstream_utils] Upgrade Sleipnir (#7512)
It now uses SQP for problems without inequality constraints, which is
faster.

main:
```
[ RUN      ] Ellipse2dTest.DistanceToPoint
0.203 ms
[       OK ] Ellipse2dTest.DistanceToPoint (0 ms)
[ RUN      ] Ellipse2dTest.FindNearestPoint
0.019 ms
[       OK ] Ellipse2dTest.FindNearestPoint (0 ms)
```

upgrade:
```
[ RUN      ] Ellipse2dTest.DistanceToPoint
0.197 ms
[       OK ] Ellipse2dTest.DistanceToPoint (0 ms)
[ RUN      ] Ellipse2dTest.FindNearestPoint
0.015 ms
[       OK ] Ellipse2dTest.FindNearestPoint (0 ms)
```
2024-12-07 23:02:39 -08:00
Tyler Veness
ae44295024 Use std::bit_cast (#7492) 2024-12-07 23:02:09 -08:00
Tyler Veness
e08fdeba21 [wpimath] Rename FindNearestPoint() to Nearest() (#7513)
This is easier to type and follows the naming of Pose2d::Nearest().

Since Ellipse2d and Rectangle2d were added for the 2025 season, we don't
need to add deprecation notices.
2024-12-07 23:01:18 -08:00
Wispy
544553a58f [developerRobot] Add Epilogue and wpiunits to developerRobot (#7510) 2024-12-07 23:00:49 -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
838c5fbcd7 [ci] Fix labeler yaml (#7523) 2024-12-07 21:37:49 -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
Peter Johnson
38a239b531 [ci] Add labeler action to auto-label PRs (#7520) 2024-12-07 21:15:49 -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
9a1b4245fa [build] Restore Windows constexpr mutex define on wpiutil (#7515) 2024-12-07 18:55:06 -08:00
Thad House
be72e0ecd8 Skip linux arm64 build on tools build (#7511) 2024-12-07 16:37:11 -08:00
Tyler Veness
e222efaa01 [wpimath] Add affine transformation constructors and getters to geometry API (#7430)
Fixes #7429.
2024-12-07 15:49:17 -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
shueja
f772bb141d [epilogue] Add extra parentheses around cast when using varhandle (#7428)
* [epilogue] Add extra parentheses around cast when using varhandle

* Fixed tests and added one for private suppliers

* Fix tests

* Formatting fixes

* Update epilogue-processor/src/test/java/edu/wpi/first/epilogue/processor/AnnotationProcessorTest.java

Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>
Co-authored-by: Sam Carlberg <sam@slfc.dev>
2024-12-07 16:09:35 -05:00
Tyler Veness
278efa6384 [upstream_utils] Remove Sleipnir patches no longer needed with GCC 11 (#7491) 2024-12-07 12:36:25 -08:00
Tyler Veness
e876452967 [ci] Upgrade to wpiformat 2024.50 (#7506) 2024-12-07 11:40:24 -08:00
Sam Carlberg
5c95966d44 Fix epilogue loading optional types from the newcommands vendordep (#7505) 2024-12-07 13:56:05 -05: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
882233bede [wpimath] Improve SimpleMotorFeedforward argument names and deprecation message (#7489)
Also roll back SimpleMotorFeedforward unit API until 2027.
2024-12-06 22:32:40 -08:00
Peter Johnson
1921d019b7 [build] Update arm builds to bookworm (#7501)
Also bumps native-utils to 2025.9.0.
And upgrades OpenCV to -3; fixes some enum conversion deprecation warnings.
2024-12-06 22:19:00 -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
Peter Johnson
c3fc7c829d [build] Upgrade OpenCV to 4.10.0 (#7500) 2024-12-06 15:41:40 -08:00
Ryan Blue
4ce8930342 [developerRobot] Add instructions for developerRobot java sim (NFC) (#7498) 2024-12-06 00:24:26 -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
Daniel Chen
60198c0bec [epilogue] Improved opt-in logging support (#7437) 2024-12-03 20:30:55 -08:00
Gold856
54f0e11fc0 [build] Update OpenCV to 2025-4.8.0-2 (#7476) 2024-12-03 20:30:03 -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
sciencewhiz
de82ed434d [hal] Add usage reporting for TOF sensors (#7470) 2024-12-01 17:02:20 -08:00
Thad House
4dd3a36d2e [ci] Switch lint task to use base ubuntu image (#7471) 2024-12-01 17:01:19 -08:00
Thad House
ce63770970 Merge branch 'main' into 2027 2024-12-01 12:40:50 -08:00
sciencewhiz
92f8c89267 [hal] Add usage reporting for 2025 legal motor controllers (#7467) 2024-11-30 22:25:04 -08:00
Thad House
1eecaaf337 [build] Update OpenCV to 2025 (#7468) 2024-11-30 22:21:41 -08:00
sciencewhiz
892e062316 [hal,wpilib,wpimath] Add Usage Reporting for Choreo and PathWeaver (#7464) 2024-11-30 20:33:09 -08:00
sciencewhiz
9807d60566 [hal,wpilib] Change Power Distribution usage reporting to Instances (#7465)
LabVIEW doesn't appear to report PDP. This should reduce the number of
Unknowns in the parsed UsageReporting.
2024-11-30 20:32:21 -08:00
Thad House
c51f65bd4f [hal] Add initial SystemServer support (#7463) 2024-11-30 20:31:26 -08:00
Thad House
c387a7ecae [build] Update to 2025 compilers (#7462) 2024-11-30 10:07:53 -08:00
Thad House
82132c3272 [hal] Initial SystemCore empty HAL (#7454) 2024-11-30 10:04:00 -08:00
Peter Johnson
715cbb6b76 [sim] GUI: Update FMS widget when real DS is connected (#7456) 2024-11-30 09:49:09 -08:00
Ryan Blue
9d40b993f8 [wpiutil] Fix HasNestedStruct docs (NFC) (#7459) 2024-11-30 07:44:19 -08:00
Sam Carlberg
92ee5bc523 [epilogue] Add usage reporting (#7461) 2024-11-30 09:50:34 -05:00
Peter Johnson
847c3120d3 Merge branch 'main' into 2027 2024-11-30 00:35:00 -08:00
Peter Johnson
5012ad7499 [epilogue] Fix missed EpilogueBackend renames (#7458) 2024-11-30 00:34:41 -08:00
Peter Johnson
7ae4333c81 [ci] Publish on 2027 repositories 2024-11-29 23:52:22 -08:00
Peter Johnson
145450b73d [ci] Disable processing of 2027 tags (#7457) 2024-11-29 23:51:27 -08:00
Nicholas Armstrong
b91864a5ec [wpilib] Fix acceleration getter for DCMotorSim (#7449) 2024-11-29 22:15:00 -08:00
sciencewhiz
0941251375 [wpilib] Add usage reporting for loggers (#7450) 2024-11-29 22:13:31 -08:00
Sam Carlberg
7d178615fa [epilogue] Allow custom loggers for generic types (#7452)
Support custom loggers for generic types
Improve error messaging for custom loggers with generic type arguments
Consistently start all epilogue processor prints with "[EPILOGUE]"
2024-11-29 22:11:46 -08:00
Sam Carlberg
806d56e564 [epilogue] Rename DataLogger to EpilogueBackend for clarity (#7453)
Update documentation and internal names correspondingly
2024-11-29 22:10:51 -08:00
Tyler Veness
65f3345407 [upstream_utils] Suppress protobuf warnings on GCC 12 too (#7451) 2024-11-29 18:19:59 -08:00
Sam Carlberg
5e1c6a84ce [wpilibj, wpilibc] Fix LED patterns not offsetting reads (#6948)
Was causing bugs when combined with patterns that need to read back from the buffer (eg masks and overlays)

Co-authored-by: Joseph Eng <s-engjo@bsd405.org>
2024-11-28 21:25:54 -08:00
Tyler Veness
a0af0fd572 [wpimath] Remove redundant internal DARE function (#7442) 2024-11-28 21:24:13 -08:00
Kavin Muralikrishnan
f377a9c573 [examples] Fix SysId example references to shooter subsystem (#7392) 2024-11-27 23:04:53 -08:00
Peter Johnson
62338c7287 [sim] Fix DS GUI System Joysticks window auto-hiding (#7431) 2024-11-27 23:03:55 -08:00
sciencewhiz
49e3e4a0be [wpiunits] Fix deprecation javadoc for units negate (#7436)
deprecated javadoc tags aren't inherited
2024-11-27 23:03:23 -08:00
Joseph Eng
59dbfc9c2d [wpimath] Improve C++ SimpleMotorFeedforward unit type support (#7440)
Allow using non-base types
Allow using angles for serde
2024-11-27 23:02:31 -08:00
Sam Carlberg
9607c6c10d [epilogue] Fix epilogue generating incorrect packages for inner classes (#7439) 2024-11-27 23:02:00 -08:00
sciencewhiz
6ef5b85758 [wpiunits] Restore and deprecate divide (#7438)
It was changed to div in #7387, but 2024 used divide.
2024-11-27 23:01:26 -08:00
Peter Johnson
b6de7acbdb [sim] GUI: Don't show Window menu if it has no contents (#7432) 2024-11-25 17:25:55 -08:00
Thad House
fe28fa1ded [wpilibj] Fix ADIS16470 Gyro (#7434) 2024-11-25 17:23:32 -08:00
Tyler Veness
b7eb9fb8f9 [upstream_utils] Add std::is_debugger_present() shim (#7423) 2024-11-22 09:17:23 -08:00
oh-yes-0-fps
1d58c5025e [wpilibj] Add procedural struct generator for enums and records (#7149) 2024-11-21 20:48:11 -08:00
Thad House
b4a8d33486 [ntcoreffi] Use static runtime for ntcoreffi (#7422)
This avoids requiring users of this library to keep up to date with the latest MSVC runtimes.
2024-11-21 16:14:12 -07:00
2498 changed files with 108218 additions and 112004 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,13 +13,19 @@ build --tool_java_runtime_version=remotejdk_17
test --test_output=errors
test --test_verbose_timeout_warnings
import shared/bazel/compiler_flags/sanitizers.rc
import shared/bazel/compiler_flags/base_linux_flags.rc
import shared/bazel/compiler_flags/linux_flags.rc
import shared/bazel/compiler_flags/osx_flags.rc
import shared/bazel/compiler_flags/roborio_flags.rc
import shared/bazel/compiler_flags/windows_flags.rc
import shared/bazel/compiler_flags/coverage_flags.rc
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
build:linuxarm32 --config=raspibookworm32
build:linuxarm64 --config=bookworm64
build:build_java --test_tag_filters=allwpilib-build-java --build_tag_filters=allwpilib-build-java
build:build_cpp --test_tag_filters=+allwpilib-build-cpp --build_tag_filters=+allwpilib-build-cpp

62
.github/actions/pregen/action.yml vendored Normal file
View File

@@ -0,0 +1,62 @@
name: 'Setup and run pregeneration'
description: 'Sets up the dependencies needed to generate generated files and runs all generation scripts'
runs:
using: "composite"
steps:
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install jinja and protobuf
run: python -m pip install jinja2 protobuf grpcio-tools
shell: bash
- name: Install protobuf and perl dependencies
run: |
sudo apt-get update
sudo apt-get install -y protobuf-compiler liblist-moreutils-perl
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 ntcore
run: ./ntcore/generate_topics.py
shell: bash
- name: Regenerate imgui
run: |
./thirdparty/imgui_suite/generate_fonts.sh
./thirdparty/imgui_suite/generate_gl3w.py
shell: bash
- name: Regenerate HIDs
run: |
./wpilibc/generate_hids.py
./wpilibj/generate_hids.py
./wpilibNewCommands/generate_hids.py
shell: bash
- name: Regenerate PWM motor controllers
run: |
./wpilibc/generate_pwm_motor_controllers.py
./wpilibj/generate_pwm_motor_controllers.py
shell: bash
- name: Regenerate mrcal minimath
run: ./wpical/generate_mrcal.py
shell: bash
- name: Regenerate wpimath
run: |
./wpimath/generate_nanopb.py
./wpimath/generate_numbers.py
./wpimath/generate_quickbuf.py --quickbuf_plugin protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
shell: bash
- name: Regenerate wpiunits
run: ./wpiunits/generate_units.py
shell: bash
- name: Regenerate wpiutil nanopb
run: ./wpiutil/generate_nanopb.py
shell: bash

62
.github/labeler.yml vendored Normal file
View File

@@ -0,0 +1,62 @@
'2027':
- base-branch: '2027'
'component: apriltag':
- changed-files:
- any-glob-to-any-file: apriltag/**
'component: command-based':
- changed-files:
- any-glob-to-any-file: wpilibNewCommands/**
'component: cscore':
- changed-files:
- any-glob-to-any-file: cscore/**
'component: datalogtool':
- changed-files:
- any-glob-to-any-file: datalogtool/**
'component: epilogue':
- changed-files:
- any-glob-to-any-file: epilogue-*/**
'component: examples':
- changed-files:
- any-glob-to-any-file: wpilib*Examples/**
'component: glass':
- changed-files:
- any-glob-to-any-file: glass/**
'component: hal':
- changed-files:
- any-glob-to-any-file: hal/**
'component: ntcore':
- changed-files:
- any-glob-to-any-file: ntcore/**
'component: outlineviewer':
- changed-files:
- any-glob-to-any-file: outlineviewer/**
'component: sysid':
- changed-files:
- any-glob-to-any-file: sysid/**
'component: wpilibc':
- changed-files:
- any-glob-to-any-file: wpilibc/**
'component: wpilibj':
- changed-files:
- any-glob-to-any-file: wpilibj/**
'component: wpimath':
- changed-files:
- any-glob-to-any-file: wpimath/**
'component: wpinet':
- changed-files:
- any-glob-to-any-file: wpinet/**
'component: wpiunits':
- changed-files:
- any-glob-to-any-file: wpiunits/**
'component: wpiutil':
- changed-files:
- any-glob-to-any-file: wpiutil/**
'component: wpical':
- changed-files:
- any-glob-to-any-file: wpical/**
'component: usage reporting':
- changed-files:
- any-glob-to-any-file: hal/src/generate/**
'attn: NI':
- changed-files:
- any-glob-to-any-file: hal/src/generate/**

View File

@@ -3,7 +3,7 @@
{
"aql": {
"items.find": {
"repo": "wpilib-mvn-development",
"repo": "wpilib-mvn-development-local",
"path": { "$nmatch":"*edu/wpi/first/thirdparty*" },
"$or":[
{

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:2024-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

@@ -1,101 +0,0 @@
name: Comment Commands
on:
issue_comment:
types: [ created ]
jobs:
format:
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/format')
runs-on: ubuntu-22.04
steps:
- name: React Rocket
uses: actions/github-script@v7
with:
script: |
const {owner, repo} = context.issue
github.rest.reactions.createForIssueComment({
owner,
repo,
comment_id: context.payload.comment.id,
content: "rocket",
});
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}
- name: Fetch all history and metadata
run: |
git checkout -b pr
git branch -f main origin/main
- name: Checkout PR
run: |
gh pr checkout $NUMBER
env:
GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}"
NUMBER: ${{ github.event.issue.number }}
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Install wpiformat
run: pip3 install wpiformat==2024.45
- name: Run wpiformat
run: wpiformat
- name: Run spotlessApply
run: ./gradlew spotlessApply
- name: Commit
run: |
# Set credentials
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Commit
git commit -am "Formatting fixes"
git push
pregen:
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/pregen')
runs-on: ubuntu-22.04
steps:
- name: React Rocket
uses: actions/github-script@v7
with:
script: |
const {owner, repo} = context.issue
github.rest.reactions.createForIssueComment({
owner,
repo,
comment_id: context.payload.comment.id,
content: "rocket",
});
- uses: actions/checkout@v4
with:
token: ${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}
- name: Checkout PR
run: |
gh pr checkout $NUMBER
env:
GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}"
NUMBER: ${{ github.event.issue.number }}
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install jinja
run: python -m pip install jinja2
- name: Install protobuf dependencies
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler && wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf-1.3.3-linux-x86_64.exe && chmod +x protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
- name: Regenerate all
run: ./.github/workflows/pregen_all.py --quickbuf_plugin=protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
- name: Commit
run: |
# Set credentials
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Commit
git commit -am "Regenerate pregenerated files"
git push

View File

@@ -12,7 +12,7 @@ env:
jobs:
publish:
name: "Documentation - Publish"
runs-on: ubuntu-22.04
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:
@@ -20,6 +20,7 @@ jobs:
with:
fetch-depth: 0
persist-credentials: false
- uses: gradle/actions/wrapper-validation@v4
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
@@ -32,12 +33,17 @@ jobs:
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=beta" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v')
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Set environment variables (Release)
run: |
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')
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

@@ -18,9 +18,9 @@ def main():
for obj in data:
out_args = []
# Filter out -isystem flags that cause false positives
iter_args = iter(obj["arguments"])
for arg in iter_args:
# Filter out -isystem flags that cause false positives
if arg == "-isystem":
next_arg = next(iter_args)
@@ -28,6 +28,9 @@ def main():
# error: conflicting types for '_mm_prefetch' [clang-diagnostic-error]
if not next_arg.startswith("/usr/lib/gcc/"):
out_args += ["-isystem", next_arg]
# Replace GCC warning argument with one Clang recognizes
elif arg == "-Wno-maybe-uninitialized":
out_args.append("-Wno-uninitialized")
else:
out_args.append(arg)

View File

@@ -1,14 +0,0 @@
name: "Validate Gradle Wrapper"
on: [pull_request, push]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v3

View File

@@ -7,25 +7,33 @@ concurrency:
cancel-in-progress: true
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v4
build-docker:
strategy:
fail-fast: false
matrix:
include:
- container: wpilib/roborio-cross-ubuntu:2024-22.04
artifact-name: Athena
build-options: "-Ponlylinuxathena"
- container: wpilib/raspbian-cross-ubuntu:bullseye-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:bullseye-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
uses: jlumbroso/free-disk-space@main
@@ -42,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')
if: startsWith(github.ref, 'refs/tags/v2027')
- name: Build with Gradle
uses: addnab/docker-run-action@v3
with:
@@ -96,12 +104,14 @@ jobs:
task: "build"
outputs: "build/allOutputs"
- os: windows-2022
artifact-name: Win32
architecture: x86
artifact-name: Win64FFI
architecture: x64
task: ":ntcoreffi:build"
build-options: "-Pntcoreffibuild -Pbuildwinarm64"
outputs: "ntcoreffi/build/outputs"
name: "Build - ${{ matrix.artifact-name }}"
runs-on: ${{ matrix.os }}
needs: [validation]
steps:
- uses: actions/checkout@v4
with:
@@ -119,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')))
(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')))
(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')
- 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'
@@ -159,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')))
(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'
@@ -173,7 +180,8 @@ jobs:
build-documentation:
name: "Build - Documentation"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
needs: [validation]
steps:
- uses: actions/checkout@v4
with:
@@ -184,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')
if: startsWith(github.ref, 'refs/tags/v2027')
- name: Build with Gradle
run: ./gradlew docs:zipDocs --build-cache -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
env:
@@ -198,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'))
(github.ref == 'refs/heads/2027' || startsWith(github.ref, 'refs/tags/v2027'))
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
@@ -216,48 +224,48 @@ jobs:
- uses: actions/checkout@v4
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(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'))
(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'))
(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'))
(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'))
(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')
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 }}
@@ -265,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'))
(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')
with:
token: ${{ secrets.TOOL_REPO_ACCESS_TOKEN }}
repository: wpilibsuite/${{ matrix.repo }}
event-type: tag
client-payload: '{"package_name": "allwpilib", "package_version": "${{ github.ref_name }}"}'

14
.github/workflows/labeler.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
labeler:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5
with:
sync-labels: true

View File

@@ -11,9 +11,16 @@ concurrency:
cancel-in-progress: true
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v4
wpiformat:
name: "wpiformat"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
@@ -27,9 +34,11 @@ jobs:
with:
python-version: '3.12'
- name: Install wpiformat
run: pip3 install wpiformat==2024.45
run: |
python -m venv ${{ runner.temp }}/wpiformat
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.33
- name: Run
run: wpiformat
run: ${{ runner.temp }}/wpiformat/bin/wpiformat
- name: Check output
run: git --no-pager diff --exit-code HEAD
- name: Generate diff
@@ -50,8 +59,9 @@ jobs:
tidy:
name: "clang-tidy"
runs-on: ubuntu-22.04
container: wpilib/roborio-cross-ubuntu:2024-22.04
runs-on: ubuntu-24.04
needs: [validation]
container: wpilib/ubuntu-base:24.04
steps:
- uses: actions/checkout@v4
with:
@@ -66,22 +76,25 @@ jobs:
with:
python-version: '3.12'
- name: Install wpiformat
run: pip3 install wpiformat==2024.45
run: |
python -m venv ${{ runner.temp }}/wpiformat
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.33
- name: Create compile_commands.json
run: |
./gradlew generateCompileCommands -Ptoolchain-optional-roboRio
./.github/workflows/fix_compile_commands.py build/TargetedCompileCommands/linuxx86-64release/compile_commands.json
./.github/workflows/fix_compile_commands.py build/TargetedCompileCommands/linuxx86-64debug/compile_commands.json
- name: List changed files
run: wpiformat -list-changed-files
run: ${{ runner.temp }}/wpiformat/bin/wpiformat -list-changed-files
- name: Run clang-tidy release
run: wpiformat -no-format -tidy-changed -compile-commands=build/TargetedCompileCommands/linuxx86-64release -vv
run: ${{ runner.temp }}/wpiformat/bin/wpiformat -no-format -tidy-changed -compile-commands=build/TargetedCompileCommands/linuxx86-64release -vv
- name: Run clang-tidy debug
run: wpiformat -no-format -tidy-changed -compile-commands=build/TargetedCompileCommands/linuxx86-64debug -vv
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
container: wpilib/ubuntu-base:22.04
runs-on: ubuntu-24.04
needs: [validation]
container: wpilib/ubuntu-base:24.04
steps:
- uses: actions/checkout@v4
with:
@@ -113,7 +126,8 @@ jobs:
documentation:
name: "Documentation"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
needs: [validation]
steps:
- uses: actions/checkout@v4
with:

View File

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

View File

@@ -13,21 +13,13 @@ concurrency:
jobs:
update:
name: "Update"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install jinja and protobuf
run: python -m pip install jinja2 protobuf grpcio-tools
- name: Install protobuf dependencies
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler && wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf-1.3.3-linux-x86_64.exe && chmod +x protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
- name: Regenerate all
run: python ./.github/workflows/pregen_all.py --quickbuf_plugin protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
- name: Run pregen
uses: ./.github/actions/pregen
- name: Add untracked files to index so they count as changes
run: git add -A
- name: Check output

View File

@@ -29,19 +29,19 @@ jobs:
ctest-env: ""
ctest-flags: ""
name: "${{ matrix.name }}"
runs-on: ubuntu-22.04
container: wpilib/roborio-cross-ubuntu:2024-22.04
runs-on: ubuntu-24.04
container: wpilib/roborio-cross-ubuntu:2025-24.04
steps:
- name: Install Dependencies
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java clang-14 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-14 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-14 -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

@@ -10,26 +10,34 @@ concurrency:
cancel-in-progress: true
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v4
build-docker:
if: (github.repository_owner == 'wpilibsuite' && github.ref == 'refs/heads/main') || github.event_name != 'schedule'
strategy:
fail-fast: false
matrix:
include:
- container: wpilib/roborio-cross-ubuntu:2024-22.04
- container: wpilib/roborio-cross-ubuntu:2025-24.04
artifact-name: Athena
build-options: "-Ponlylinuxathena"
- container: wpilib/raspbian-cross-ubuntu:bullseye-22.04
- container: wpilib/raspbian-cross-ubuntu:bookworm-24.04
artifact-name: Arm32
build-options: "-Ponlylinuxarm32"
- container: wpilib/aarch64-cross-ubuntu:bullseye-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
uses: jlumbroso/free-disk-space@main
@@ -94,13 +102,9 @@ 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]
steps:
- uses: actions/checkout@v4
with:
@@ -122,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:
@@ -29,122 +29,16 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: gradle/actions/wrapper-validation@v4
- name: Build WPILib with Gradle
uses: addnab/docker-run-action@v3
with:
image: wpilib/roborio-cross-ubuntu:2024-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 :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
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

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:
@@ -120,12 +120,6 @@ jobs:
./mpack.py clone
./mpack.py copy-src
./mpack.py format-patch
- name: Run stack_walker.py
run: |
cd upstream_utils
./stack_walker.py clone
./stack_walker.py copy-src
./stack_walker.py format-patch
- name: Run memory.py
run: |
cd upstream_utils

3
.gitignore vendored
View File

@@ -255,3 +255,6 @@ bazel_auth.rc
# ctest
/Testing/
# Meson
.meson-subproject*

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

@@ -76,6 +76,7 @@ cmake_dependent_option(
)
option(WITH_CSCORE "Build cscore (needs OpenCV)" ON)
option(WITH_NTCORE "Build ntcore" ON)
option(WITH_WPICAL "Build wpical" OFF)
option(WITH_WPIMATH "Build wpimath" ON)
cmake_dependent_option(
WITH_WPIUNITS
@@ -142,6 +143,11 @@ if(WITH_DOCS)
include(AddDoxygenDocs)
add_doxygen_docs()
endif()
if(WITH_WPICAL)
find_package(Ceres CONFIG REQUIRED)
endif()
find_package(LIBSSH CONFIG 0.7.1)
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
@@ -285,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)")
@@ -314,8 +322,10 @@ if(WITH_GUI)
add_subdirectory(glass)
add_subdirectory(outlineviewer)
add_subdirectory(sysid)
if(WITH_WPICAL)
add_subdirectory(wpical)
endif()
if(LIBSSH_FOUND)
add_subdirectory(roborioteamnumbersetter)
add_subdirectory(datalogtool)
endif()
endif()

View File

@@ -1,69 +1,47 @@
# Contributor Covenant Code of Conduct
# Contributor Community Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
As members, contributors, and leaders, we commit to fostering a community where everyone feels safe, respected, and valued. We are dedicated to ensuring that participation in this community is harassment-free, inclusive, and welcoming, regardless of age, body type, abilities (visible or invisible), ethnicity, gender identity or expression, sexual orientation, socioeconomic background, education, nationality, personal appearance, race, or religion.
Above all, we pledge to act with integrity, kindness, and empathy—striving to be not only good participants but also good humans.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
Positive and respectful behavior is essential to creating a thriving community. This includes:
* Exhibiting Gracious Professionalism® at all times. Gracious Professionalism
* Practicing **Gracious Professionalism®** at all times. Gracious Professionalism
is a way of doing things that encourages high-quality work, emphasizes the
value of others, and respects individuals and the community.
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
* Showing empathy, kindness, and patience.
* Respecting diverse perspectives and experiences.
* Giving and receiving constructive feedback with openness and humility.
* Owning mistakes, apologizing when necessary, and learning from them.
* Prioritizing the well-being and success of the entire community over individual interests.
Examples of unacceptable behavior include:
Unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Using sexualized language, imagery, or making inappropriate advances
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
* Harassment in any form, whether public or private.
* Sharing private information (e.g., email or physical addresses) without explicit consent.
* Any behavior that is unprofessional, harmful, or exclusionary.
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
Community leaders are responsible for maintaining these standards and will take appropriate action to address any behavior deemed harmful, threatening, or inappropriate. Actions may include removing content, issuing warnings, or, when necessary, banning individuals. Moderation decisions will be communicated transparently where appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
This Code of Conduct applies to all community spaces, events, and instances where individuals represent the community (e.g., official email accounts, social media posts, or in-person/virtual events).
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[conduct@wpilib.org](mailto:conduct@wpilib.org).
[wpilib@wpi.edu](mailto:wpilib@wpi.edu).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
@@ -115,6 +93,9 @@ individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## A Note on Kindness
Building a community isnt just about rules—its about connection. Every interaction is an opportunity to be understanding, compassionate, and supportive. Being a good human is the key to our ethos.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],

View File

@@ -80,7 +80,7 @@ xₖ₊₁ = Axₖ + Buₖ
Changes should be submitted as a Pull Request against the main branch of WPILib. For most changes, commits will be squashed upon merge. For particularly large changes, multiple commits are ok, but assume one commit unless asked otherwise. We may ask you to break a PR into multiple standalone PRs or commits for rebase within one PR to separate unrelated changes. No change will be merged unless it is up to date with the current main branch. We do this to make sure that the git history isn't too cluttered.
During the build season, breaking changes or other changes intended for the next season can be created as a pull request against the development branch of WPILib. After the season is over, the changes in the development branch will be merged into main.
Particularly large and/or breaking changes should be targeted to the 2027 branch, which targets the [SystemCore Robot Controller](https://community.firstinspires.org/introducing-the-future-mobile-robot-controller). The intent is minimize changes for 2026, to allow development to focus on preparing for 2027.
### Merge Process

View File

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

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
@@ -59,6 +59,8 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
On macOS ARM, run `softwareupdate --install-rosetta`. This is necessary to be able to use the macOS x86 roboRIO toolchain on ARM.
On linux, run `sudo apt install gfortran`. This is necessary to be able to build WPIcal on linux platforms.
## Setup
Clone the WPILib repository and follow the instructions above for installing any required tooling. The build process uses versioning information from git. Downloading the source is not sufficient to run the build.
@@ -134,6 +136,9 @@ If you have installed the FRC Toolchain to a directory other than the default, o
Once a PR has been submitted, formatting can be run in CI by commenting `/format` on the PR. A new commit will be pushed with the formatting changes.
> [!NOTE]
> The `/format` action has been temporarily disabled. The individual formatting commands can be run locally as shown below. Alternately, the Lint and Format action for a PR will upload a patch file that can be downloaded and applied manually.
#### wpiformat
wpiformat can be executed anywhere in the repository via `py -3 -m wpiformat` on Windows or `python3 -m wpiformat` on other platforms.

View File

@@ -15,7 +15,8 @@ licenses, and/or restrictions:
Program Locations
------- ---------
Google Test gtest
Google Test thirdparty/googletest/include
thirdparty/googletest/src
LLVM wpiutil/src/main/native/thirdparty/llvm
wpiutil/src/test/native/cpp/llvm/
JSON for Modern C++ wpiutil/src/main/native/thirdparty/json
@@ -32,17 +33,29 @@ jQuery wpinet/src/main/native/resources/jquery-*
popper.js wpinet/src/main/native/resources/popper-*
units wpimath/src/main/native/include/units/
Eigen wpimath/src/main/native/thirdparty/eigen/include/
StackWalker wpiutil/src/main/native/windows/StackWalker.*
GHC filesystem wpiutil/src/main/native/thirdparty/include/wpi/ghc/
Team 254 Library wpilibj/src/main/java/edu/wpi/first/wpilibj/spline/SplineParameterizer.java
wpilibj/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryParameterizer.java
wpilibc/src/main/native/include/spline/SplineParameterizer.h
wpilibc/src/main/native/include/trajectory/TrajectoryParameterizer.h
wpilibc/src/main/native/cpp/trajectory/TrajectoryParameterizer.cpp
Team 254 Library wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java
wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java
wpimath/src/main/native/include/frc/spline/SplineParameterizer.h
wpimath/src/main/native/include/frc/trajectory/TrajectoryParameterizer.h
wpimath/src/main/native/cpp/trajectory/TrajectoryParameterizer.cpp
Portable File Dialogs wpigui/src/main/native/include/portable-file-dialogs.h
V8 export-template wpiutil/src/main/native/include/wpi/SymbolExports.h
GCEM wpimath/src/main/native/thirdparty/gcem/include/
Sleipnir wpimath/src/main/native/thirdparty/sleipnir
Debugging wpiutil/src/main/native/thirdparty/debugging
argparse wpiutil/src/main/native/thirdparty/argparse/include/wpi/argparse.h
apriltag apriltag/src/main/native/thirdparty/apriltag
glfw thirdparty/imgui_suite/glfw
Dear ImGui thirdparty/imgui_suite/imgui
implot thirdparty/imgui_suite/implot
memory wpiutil/src/main/native/thirdparty/memory
nanopb wpiutil/src/main/native/thirdparty/nanopb
protobuf wpiutil/src/main/native/thirdparty/protobuf
mrcal wpical/src/main/native/thirdparty/mrcal
libdogleg wpical/src/main/native/thirdparty/libdogleg
Simd hal/src/main/native/athena/simd
Additionally, glfw, memory, and nanopb were all modified for use in WPILib.
==============================================================================
Google Test License
@@ -461,7 +474,7 @@ limitations under the License.
==============================================================================
MPacks License
MPack License
==============================================================================
The MIT License (MIT)
@@ -1012,35 +1025,6 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice
defined by the Mozilla Public License, v. 2.0.
===================
StackWalker License
===================
Copyright (c) 2005-2013, Jochen Kalmbach
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
Neither the name of Jochen Kalmbach nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================
Team 254 Library
================
@@ -1077,33 +1061,6 @@ and/or modify it under the terms of the Do What the **** You Want
to Public License, Version 2, as published by the WTFPL Task Force.
See http://www.wtfpl.net/ for more details.
======================
Boost Software License
======================
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
======
fmtlib
======
@@ -1135,29 +1092,6 @@ of this Software are embedded into a machine-executable object form of such
source code, you may redistribute such embedded portions in such object form
without including the above copyright and permission notices.
==============
GHC filesystem
==============
Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
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.
==================
V8 export-template
==================
@@ -1224,3 +1158,544 @@ Redistribution and use in source and binary forms, with or without modification,
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=================
Debugging License
=================
MIT License
Copyright (c) 2021-2022 René Ferdinand Rivera Morell
Copyright (c) 2018 Isabella Muerte
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.
================
argparse License
================
Copyright (c) 2018 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>
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.
================
apriltag License
================
BSD 2-Clause License
Copyright (C) 2013-2016, The Regents of The University of Michigan.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the Regents of The University of Michigan.
============
gl3w License
============
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
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 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.
============
glfw License
============
Copyright (c) 2002-2006 Marcus Geelnard
Copyright (c) 2006-2019 Camilla Löwy
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would
be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
==================
Dear ImGui License
==================
The MIT License (MIT)
Copyright (c) 2014-2024 Omar Cornut
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.
==============
implot License
==============
MIT License
Copyright (c) 2020 Evan Pezent
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.
==============
memory License
==============
Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
This software is provided 'as-is', without any express or
implied warranty. In no event will the authors be held
liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute
it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented;
you must not claim that you wrote the original software.
If you use this software in a product, an acknowledgment
in the product documentation would be appreciated but
is not required.
2. Altered source versions must be plainly marked as such,
and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any
source distribution.
==============
nanopb License
==============
Copyright (c) 2011 Petteri Aimonen <jpa at nanopb.mail.kapsi.fi>
This software is provided 'as-is', without any express or
implied warranty. In no event will the authors be held liable
for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
================
protobuf License
================
Copyright 2008 Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Code generated by the Protocol Buffer compiler is owned by the owner
of the input file used when generating it. This code is not
standalone and requires a support library to be linked with it. This
support library is itself covered by the above license.
===========
stb License
===========
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
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.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
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 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.
=============
mrcal License
=============
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2017-2023 California Institute of Technology ("Caltech"). U.S.
Government sponsorship acknowledged. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=================
libdogleg License
=================
Copyright 2011 Oblong Industries 2017 Dima Kogan <dima@secretsauce.net>
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
The full text of the license is available at http://www.gnu.org/licenses
============
Simd License
============
MIT License
Copyright (c) 2011-2017 Ihar Yermalayeu
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.

214
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,22 +52,105 @@ 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",
sha256 = "2ef1cafce7f4fd4e909bb5de8b0dc771a934646afd55d5f100ff31f6b500df98",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_toolchains/releases/download/2024-1.bcr1/rules_bzlmodRio_toolchains-2024-1.bcr1.tar.gz",
sha256 = "ff25b5f9445cbd43759be4c6582b987d1065cf817c593eedc7ada1a699298c84",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_toolchains/releases/download/2025-1.bcr2/rules_bzlmodRio_toolchains-2025-1.bcr2.tar.gz",
)
load("@rules_bzlmodrio_toolchains//:maven_deps.bzl", "setup_legacy_setup_toolchains_dependencies")
@@ -50,8 +164,8 @@ load_toolchains()
#
http_archive(
name = "rules_bzlmodrio_jdk",
sha256 = "a00d5fa971fbcad8a17b1968cdc5350688397035e90b0cb94e040d375ecd97b4",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_jdk/releases/download/17.0.8.1-1/rules_bzlmodRio_jdk-17.0.8.1-1.tar.gz",
sha256 = "81869fe9860e39b17e4a9bc1d33c1ca2faede7e31d9538ed0712406f753a2163",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_jdk/releases/download/17.0.12-7/rules_bzlmodRio_jdk-17.0.12-7.tar.gz",
)
load("@rules_bzlmodrio_jdk//:maven_deps.bzl", "setup_legacy_setup_jdk_dependencies")
@@ -62,23 +176,35 @@ register_toolchains(
"@local_roborio//:macos",
"@local_roborio//:linux",
"@local_roborio//:windows",
"@local_raspi_32//:macos",
"@local_raspi_32//:linux",
"@local_raspi_32//:windows",
"@local_systemcore//:macos",
"@local_systemcore//:linux",
"@local_systemcore//:windows",
"@local_raspi_bullseye_32//:macos",
"@local_raspi_bullseye_32//:linux",
"@local_raspi_bullseye_32//:windows",
"@local_raspi_bookworm_32//:macos",
"@local_raspi_bookworm_32//:linux",
"@local_raspi_bookworm_32//:windows",
"@local_bullseye_32//:macos",
"@local_bullseye_32//:linux",
"@local_bullseye_32//:windows",
"@local_bullseye_64//:macos",
"@local_bullseye_64//:linux",
"@local_bullseye_64//:windows",
"@local_bookworm_32//:macos",
"@local_bookworm_32//:linux",
"@local_bookworm_32//:windows",
"@local_bookworm_64//:macos",
"@local_bookworm_64//:linux",
"@local_bookworm_64//:windows",
)
setup_legacy_setup_jdk_dependencies()
http_archive(
name = "bzlmodrio-ni",
sha256 = "197fceac88bf44fb8427d5e000b0083118d3346172dd2ad31eccf83a5e61b3ce",
url = "https://github.com/wpilibsuite/bzlmodRio-ni/releases/download/2025.0.0/bzlmodRio-ni-2025.0.0.tar.gz",
sha256 = "fff62c3cb3e83f9a0d0a01f1739477c9ca5e9a6fac05be1ad59dafcd385801f7",
url = "https://github.com/wpilibsuite/bzlmodRio-ni/releases/download/2025.2.0/bzlmodRio-ni-2025.2.0.tar.gz",
)
load("@bzlmodrio-ni//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_ni_cpp_dependencies")
@@ -87,8 +213,8 @@ setup_legacy_bzlmodrio_ni_cpp_dependencies()
http_archive(
name = "bzlmodrio-opencv",
sha256 = "5314cce05b49451a46bf3e3140fc401342e53d5f3357612ed4473e59bb616cba",
url = "https://github.com/wpilibsuite/bzlmodRio-opencv/releases/download/2024.4.8.0-4.bcr1/bzlmodRio-opencv-2024.4.8.0-4.bcr1.tar.gz",
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")
@@ -98,3 +224,67 @@ setup_legacy_bzlmodrio_opencv_cpp_dependencies()
load("@bzlmodrio-opencv//:maven_java_deps.bzl", "setup_legacy_bzlmodrio_opencv_java_dependencies")
setup_legacy_bzlmodrio_opencv_java_dependencies()
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",
url = "https://github.com/bazelbuild/apple_support/releases/download/1.15.1/apple_support.1.15.1.tar.gz",
)
load(
"@build_bazel_apple_support//lib:repositories.bzl",
"apple_support_dependencies",
)
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()

141
apriltag/BUILD.bazel Normal file
View File

@@ -0,0 +1,141 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "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")
load("//shared/bazel/rules/gen:gen-resources.bzl", "generate_resources")
cc_library(
name = "thirdparty-apriltag",
srcs = glob(["src/main/native/thirdparty/apriltag/src/**"]),
hdrs = glob(["src/main/native/thirdparty/apriltag/include/**"]),
copts = select({
"@bazel_tools//src/conditions:darwin": [
"-Wno-format-nonliteral",
"-Wno-gnu-zero-variadic-macro-arguments",
"-Wno-uninitialized",
"-Wno-sign-compare",
"-Wno-type-limits",
],
"@bazel_tools//src/conditions:windows": [
"/wd4005",
"/wd4018",
"/wd4244",
"/wd4267",
"/wd4996",
],
"@rules_bzlmodrio_toolchains//constraints/combined:is_linux": [
"-Wno-format-nonliteral",
"-Wno-maybe-uninitialized",
"-Wno-sign-compare",
"-Wno-type-limits",
],
}),
includes = ["src/main/native/thirdparty/apriltag/include/common"],
strip_include_prefix = "src/main/native/thirdparty/apriltag/include",
)
generate_resources(
name = "generate-resources",
namespace = "frc",
prefix = "APRILTAG",
resource_files = glob(["src/main/native/resources/**"]),
)
wpilib_cc_library(
name = "apriltag.static",
srcs = [":generate-resources"] + glob(
["src/main/native/cpp/**"],
exclude = ["src/main/native/cpp/jni/**"],
),
hdrs = glob(["src/main/native/include/**/*"]),
defines = ["WPILIB_EXPORTS"],
strip_include_prefix = "src/main/native/include",
visibility = ["//visibility:public"],
deps = [
":thirdparty-apriltag",
"//wpimath:wpimath.static",
"//wpiutil:wpiutil.static",
],
)
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"],
deps = [
"//wpimath:wpimath-java",
"//wpiutil:wpiutil-java",
"@bzlmodrio-opencv//libraries/java/opencv",
"@maven//:com_fasterxml_jackson_core_jackson_annotations",
"@maven//:com_fasterxml_jackson_core_jackson_core",
"@maven//:com_fasterxml_jackson_core_jackson_databind",
],
)
cc_test(
name = "apriltag-cpp-test",
size = "small",
srcs = glob(["src/test/native/cpp/**"]),
tags = [
"no-asan",
],
deps = [
":apriltag.static",
"//thirdparty/googletest:googletest.static",
],
)
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"],
deps = [
":apriltag.static",
],
)
java_binary(
name = "DevMain-Java",
srcs = ["src/dev/java/edu/wpi/first/apriltag/DevMain.java"],
main_class = "edu.wpi.first.apriltag.DevMain",
deps = [
":apriltag-java",
],
)
py_binary(
name = "convert_apriltag_layouts",
srcs = ["convert_apriltag_layouts.py"],
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

@@ -7,10 +7,11 @@ AprilTagFields expects.
The input CSV has the following format:
* Columns: ID, X, Y, Z, Rotation
* Columns: ID, X, Y, Z, Z Rotation, Y Rotation
* ID is a positive integer
* X, Y, and Z are decimal inches
* Rotation is yaw in degrees
* Z Rotation is yaw in degrees
* Y Rotation is pitch in degrees
The values come from a table in the layout marking diagram (e.g.,
https://firstfrc.blob.core.windows.net/frc2024/FieldAssets/2024LayoutMarkingDiagram.pdf).
@@ -48,13 +49,14 @@ def main():
x = float(row[1])
y = float(row[2])
z = float(row[3])
rotation = float(row[4])
zRotation = float(row[4])
yRotation = float(row[5])
# Turn yaw into quaternion
q = geometry.Rotation3d(
units.radians(0.0),
units.radians(0.0),
units.degreesToRadians(rotation),
units.radians(0),
units.degreesToRadians(yRotation),
units.degreesToRadians(zRotation),
).getQuaternion()
json_data["tags"].append(

View File

@@ -13,13 +13,17 @@ public enum AprilTagFields {
/** 2023 Charged Up. */
k2023ChargedUp("2023-chargedup.json"),
/** 2024 Crescendo. */
k2024Crescendo("2024-crescendo.json");
k2024Crescendo("2024-crescendo.json"),
/** 2025 Reefscape Welded (see TU 12). */
k2025ReefscapeWelded("2025-reefscape-welded.json"),
/** 2025 Reefscape AndyMark (see TU 12). */
k2025ReefscapeAndyMark("2025-reefscape-andymark.json");
/** Base resource directory. */
public static final String kBaseResourceDir = "/edu/wpi/first/apriltag/";
/** Alias to the current game. */
public static final AprilTagFields kDefaultField = k2024Crescendo;
public static final AprilTagFields kDefaultField = k2025ReefscapeWelded;
/** Resource filename. */
public final String m_resourceFile;

View File

@@ -133,6 +133,8 @@ namespace frc {
std::string_view GetResource_2022_rapidreact_json();
std::string_view GetResource_2023_chargedup_json();
std::string_view GetResource_2024_crescendo_json();
std::string_view GetResource_2025_reefscape_welded_json();
std::string_view GetResource_2025_reefscape_andymark_json();
} // namespace frc
@@ -148,6 +150,12 @@ AprilTagFieldLayout AprilTagFieldLayout::LoadField(AprilTagField field) {
case AprilTagField::k2024Crescendo:
fieldString = GetResource_2024_crescendo_json();
break;
case AprilTagField::k2025ReefscapeWelded:
fieldString = GetResource_2025_reefscape_welded_json();
break;
case AprilTagField::k2025ReefscapeAndyMark:
fieldString = GetResource_2025_reefscape_andymark_json();
break;
case AprilTagField::kNumFields:
throw std::invalid_argument("Invalid Field");
}

View File

@@ -20,6 +20,12 @@ enum class AprilTagField {
k2023ChargedUp,
/// 2024 Crescendo.
k2024Crescendo,
/// 2025 Reefscape AndyMark (see TU12).
k2025ReefscapeAndyMark,
/// 2025 Reefscape Welded (see TU12).
k2025ReefscapeWelded,
/// Alias to the current game.
kDefaultField = k2025ReefscapeWelded,
// This is a placeholder for denoting the last supported field. This should
// always be the last entry in the enum and should not be used by users

View File

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

View File

@@ -0,0 +1,23 @@
ID,X,Y,Z,Z-Rotation,X-Rotation
1,656.98,24.73,58.5,126,0
2,656.98,291.9,58.5,234,0
3,452.4,316.21,51.25,270,0
4,365.2,241.44,73.54,0,30
5,365.2,75.19,73.54,0,30
6,530.49,129.97,12.13,300,0
7,546.87,158.3,12.13,0,0
8,530.49,186.63,12.13,60,0
9,497.77,186.63,12.13,120,0
10,481.39,158.3,12.13,180,0
11,497.77,129.97,12.13,240,0
12,33.91,24.73,58.5,54,0
13,33.91,291.9,58.5,306,0
14,325.68,241.44,73.54,180,30
15,325.68,75.19,73.54,180,30
16,238.49,0.42,51.25,90,0
17,160.39,129.97,12.13,240,0
18,144,158.3,12.13,180,0
19,160.39,186.63,12.13,120,0
20,193.1,186.63,12.13,60,0
21,209.49,158.3,12.13,0,0
22,193.1,129.97,12.13,300,0
1 ID X Y Z Z-Rotation X-Rotation
2 1 656.98 24.73 58.5 126 0
3 2 656.98 291.9 58.5 234 0
4 3 452.4 316.21 51.25 270 0
5 4 365.2 241.44 73.54 0 30
6 5 365.2 75.19 73.54 0 30
7 6 530.49 129.97 12.13 300 0
8 7 546.87 158.3 12.13 0 0
9 8 530.49 186.63 12.13 60 0
10 9 497.77 186.63 12.13 120 0
11 10 481.39 158.3 12.13 180 0
12 11 497.77 129.97 12.13 240 0
13 12 33.91 24.73 58.5 54 0
14 13 33.91 291.9 58.5 306 0
15 14 325.68 241.44 73.54 180 30
16 15 325.68 75.19 73.54 180 30
17 16 238.49 0.42 51.25 90 0
18 17 160.39 129.97 12.13 240 0
19 18 144 158.3 12.13 180 0
20 19 160.39 186.63 12.13 120 0
21 20 193.1 186.63 12.13 60 0
22 21 209.49 158.3 12.13 0 0
23 22 193.1 129.97 12.13 300 0

View File

@@ -0,0 +1,404 @@
{
"tags": [
{
"ID": 1,
"pose": {
"translation": {
"x": 16.687292,
"y": 0.628142,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": 0.4539904997395468,
"X": 0.0,
"Y": 0.0,
"Z": 0.8910065241883678
}
}
}
},
{
"ID": 2,
"pose": {
"translation": {
"x": 16.687292,
"y": 7.414259999999999,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": -0.45399049973954675,
"X": -0.0,
"Y": 0.0,
"Z": 0.8910065241883679
}
}
}
},
{
"ID": 3,
"pose": {
"translation": {
"x": 11.49096,
"y": 8.031733999999998,
"z": 1.30175
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 4,
"pose": {
"translation": {
"x": 9.276079999999999,
"y": 6.132575999999999,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 0.9659258262890683,
"X": 0.0,
"Y": 0.25881904510252074,
"Z": 0.0
}
}
}
},
{
"ID": 5,
"pose": {
"translation": {
"x": 9.276079999999999,
"y": 1.9098259999999998,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 0.9659258262890683,
"X": 0.0,
"Y": 0.25881904510252074,
"Z": 0.0
}
}
}
},
{
"ID": 6,
"pose": {
"translation": {
"x": 13.474446,
"y": 3.3012379999999997,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.8660254037844387,
"X": -0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 7,
"pose": {
"translation": {
"x": 13.890498,
"y": 4.0208200000000005,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 8,
"pose": {
"translation": {
"x": 13.474446,
"y": 4.740402,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 9,
"pose": {
"translation": {
"x": 12.643358,
"y": 4.740402,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 10,
"pose": {
"translation": {
"x": 12.227305999999999,
"y": 4.0208200000000005,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 11,
"pose": {
"translation": {
"x": 12.643358,
"y": 3.3012379999999997,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.4999999999999998,
"X": -0.0,
"Y": 0.0,
"Z": 0.8660254037844387
}
}
}
},
{
"ID": 12,
"pose": {
"translation": {
"x": 0.8613139999999999,
"y": 0.628142,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": 0.8910065241883679,
"X": 0.0,
"Y": 0.0,
"Z": 0.45399049973954675
}
}
}
},
{
"ID": 13,
"pose": {
"translation": {
"x": 0.8613139999999999,
"y": 7.414259999999999,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": -0.8910065241883678,
"X": -0.0,
"Y": 0.0,
"Z": 0.45399049973954686
}
}
}
},
{
"ID": 14,
"pose": {
"translation": {
"x": 8.272272,
"y": 6.132575999999999,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 5.914589856893349e-17,
"X": -0.25881904510252074,
"Y": 1.5848095757158825e-17,
"Z": 0.9659258262890683
}
}
}
},
{
"ID": 15,
"pose": {
"translation": {
"x": 8.272272,
"y": 1.9098259999999998,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 5.914589856893349e-17,
"X": -0.25881904510252074,
"Y": 1.5848095757158825e-17,
"Z": 0.9659258262890683
}
}
}
},
{
"ID": 16,
"pose": {
"translation": {
"x": 6.057646,
"y": 0.010667999999999999,
"z": 1.30175
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 17,
"pose": {
"translation": {
"x": 4.073905999999999,
"y": 3.3012379999999997,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.4999999999999998,
"X": -0.0,
"Y": 0.0,
"Z": 0.8660254037844387
}
}
}
},
{
"ID": 18,
"pose": {
"translation": {
"x": 3.6576,
"y": 4.0208200000000005,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 19,
"pose": {
"translation": {
"x": 4.073905999999999,
"y": 4.740402,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 20,
"pose": {
"translation": {
"x": 4.904739999999999,
"y": 4.740402,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 21,
"pose": {
"translation": {
"x": 5.321046,
"y": 4.0208200000000005,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 22,
"pose": {
"translation": {
"x": 4.904739999999999,
"y": 3.3012379999999997,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.8660254037844387,
"X": -0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
}
],
"field": {
"length": 17.548,
"width": 8.042
}
}

View File

@@ -0,0 +1,23 @@
ID,X,Y,Z,Z-Rotation,X-Rotation
1,657.37,25.8,58.5,126,0
2,657.37,291.2,58.5,234,0
3,455.15,317.15,51.25,270,0
4,365.2,241.64,73.54,0,30
5,365.2,75.39,73.54,0,30
6,530.49,130.17,12.13,300,0
7,546.87,158.5,12.13,0,0
8,530.49,186.83,12.13,60,0
9,497.77,186.83,12.13,120,0
10,481.39,158.5,12.13,180,0
11,497.77,130.17,12.13,240,0
12,33.51,25.8,58.5,54,0
13,33.51,291.2,58.5,306,0
14,325.68,241.64,73.54,180,30
15,325.68,75.39,73.54,180,30
16,235.73,-0.15,51.25,90,0
17,160.39,130.17,12.13,240,0
18,144,158.5,12.13,180,0
19,160.39,186.83,12.13,120,0
20,193.1,186.83,12.13,60,0
21,209.49,158.5,12.13,0,0
22,193.1,130.17,12.13,300,0
1 ID X Y Z Z-Rotation X-Rotation
2 1 657.37 25.8 58.5 126 0
3 2 657.37 291.2 58.5 234 0
4 3 455.15 317.15 51.25 270 0
5 4 365.2 241.64 73.54 0 30
6 5 365.2 75.39 73.54 0 30
7 6 530.49 130.17 12.13 300 0
8 7 546.87 158.5 12.13 0 0
9 8 530.49 186.83 12.13 60 0
10 9 497.77 186.83 12.13 120 0
11 10 481.39 158.5 12.13 180 0
12 11 497.77 130.17 12.13 240 0
13 12 33.51 25.8 58.5 54 0
14 13 33.51 291.2 58.5 306 0
15 14 325.68 241.64 73.54 180 30
16 15 325.68 75.39 73.54 180 30
17 16 235.73 -0.15 51.25 90 0
18 17 160.39 130.17 12.13 240 0
19 18 144 158.5 12.13 180 0
20 19 160.39 186.83 12.13 120 0
21 20 193.1 186.83 12.13 60 0
22 21 209.49 158.5 12.13 0 0
23 22 193.1 130.17 12.13 300 0

View File

@@ -0,0 +1,404 @@
{
"tags": [
{
"ID": 1,
"pose": {
"translation": {
"x": 16.697198,
"y": 0.65532,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": 0.4539904997395468,
"X": 0.0,
"Y": 0.0,
"Z": 0.8910065241883678
}
}
}
},
{
"ID": 2,
"pose": {
"translation": {
"x": 16.697198,
"y": 7.3964799999999995,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": -0.45399049973954675,
"X": -0.0,
"Y": 0.0,
"Z": 0.8910065241883679
}
}
}
},
{
"ID": 3,
"pose": {
"translation": {
"x": 11.560809999999998,
"y": 8.05561,
"z": 1.30175
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 4,
"pose": {
"translation": {
"x": 9.276079999999999,
"y": 6.137656,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 0.9659258262890683,
"X": 0.0,
"Y": 0.25881904510252074,
"Z": 0.0
}
}
}
},
{
"ID": 5,
"pose": {
"translation": {
"x": 9.276079999999999,
"y": 1.914906,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 0.9659258262890683,
"X": 0.0,
"Y": 0.25881904510252074,
"Z": 0.0
}
}
}
},
{
"ID": 6,
"pose": {
"translation": {
"x": 13.474446,
"y": 3.3063179999999996,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.8660254037844387,
"X": -0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 7,
"pose": {
"translation": {
"x": 13.890498,
"y": 4.0259,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 8,
"pose": {
"translation": {
"x": 13.474446,
"y": 4.745482,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 9,
"pose": {
"translation": {
"x": 12.643358,
"y": 4.745482,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 10,
"pose": {
"translation": {
"x": 12.227305999999999,
"y": 4.0259,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 11,
"pose": {
"translation": {
"x": 12.643358,
"y": 3.3063179999999996,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.4999999999999998,
"X": -0.0,
"Y": 0.0,
"Z": 0.8660254037844387
}
}
}
},
{
"ID": 12,
"pose": {
"translation": {
"x": 0.851154,
"y": 0.65532,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": 0.8910065241883679,
"X": 0.0,
"Y": 0.0,
"Z": 0.45399049973954675
}
}
}
},
{
"ID": 13,
"pose": {
"translation": {
"x": 0.851154,
"y": 7.3964799999999995,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": -0.8910065241883678,
"X": -0.0,
"Y": 0.0,
"Z": 0.45399049973954686
}
}
}
},
{
"ID": 14,
"pose": {
"translation": {
"x": 8.272272,
"y": 6.137656,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 5.914589856893349e-17,
"X": -0.25881904510252074,
"Y": 1.5848095757158825e-17,
"Z": 0.9659258262890683
}
}
}
},
{
"ID": 15,
"pose": {
"translation": {
"x": 8.272272,
"y": 1.914906,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 5.914589856893349e-17,
"X": -0.25881904510252074,
"Y": 1.5848095757158825e-17,
"Z": 0.9659258262890683
}
}
}
},
{
"ID": 16,
"pose": {
"translation": {
"x": 5.9875419999999995,
"y": -0.0038099999999999996,
"z": 1.30175
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 17,
"pose": {
"translation": {
"x": 4.073905999999999,
"y": 3.3063179999999996,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.4999999999999998,
"X": -0.0,
"Y": 0.0,
"Z": 0.8660254037844387
}
}
}
},
{
"ID": 18,
"pose": {
"translation": {
"x": 3.6576,
"y": 4.0259,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 19,
"pose": {
"translation": {
"x": 4.073905999999999,
"y": 4.745482,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 20,
"pose": {
"translation": {
"x": 4.904739999999999,
"y": 4.745482,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 21,
"pose": {
"translation": {
"x": 5.321046,
"y": 4.0259,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 22,
"pose": {
"translation": {
"x": 4.904739999999999,
"y": 3.3063179999999996,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.8660254037844387,
"X": -0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
}
],
"field": {
"length": 17.548,
"width": 8.052
}
}

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,14 +11,14 @@ 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'
id 'idea'
id 'visual-studio'
id 'net.ltgt.errorprone' version '3.1.0' apply false
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'com.gradleup.shadow' version '8.3.4' apply false
id 'com.diffplug.spotless' version '6.20.0' apply false
id 'com.github.spotbugs' version '6.0.2' apply false
}
@@ -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.3.0"
implementation "edu.wpi.first:native-utils:2025.12.1"
}

View File

@@ -28,7 +28,7 @@ public class WPIJREArtifact extends MavenArtifact {
private boolean checkJreVersion = true;
private final String artifactLocation = "edu.wpi.first.jdk:roborio-2024:17.0.9u7-1"
private final String artifactLocation = "edu.wpi.first.jdk:roborio-2024:17.0.9u7-3"
@Inject
public WPIJREArtifact(String name, RemoteTarget target) {

View File

@@ -1,5 +1,22 @@
load("@rules_cc//cc:defs.bzl", "cc_binary")
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")
wpilib_cc_library(
name = "cameraserver.static",
srcs = glob(["src/main/native/cpp/**"]),
hdrs = glob(["src/main/native/include/**/*"]),
includes = [
"cpp",
"src/main/native/include",
],
strip_include_prefix = "src/main/native/include",
visibility = ["//visibility:public"],
deps = [
"//cscore:cscore.static",
"//ntcore:ntcore.static",
],
)
java_library(
name = "cameraserver-java",
@@ -16,10 +33,21 @@ java_library(
],
)
cc_test(
name = "cameraserver-cpp-test",
size = "small",
srcs = glob(["src/test/native/**"]),
deps = [
":cameraserver.static",
"//thirdparty/googletest:googletest.static",
],
)
cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
deps = [
":cameraserver.static",
],
)

View File

@@ -22,7 +22,7 @@ application {
mainClass = 'edu.wpi.Main'
}
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'com.gradleup.shadow'
repositories {
maven {
@@ -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

@@ -14,18 +14,20 @@ macro(wpilib_target_warnings target)
target_compile_options(${target} PRIVATE ${WARNING_FLAGS})
else()
target_compile_options(
${target}
PRIVATE
/wd4146
/wd4244
/wd4251
/wd4267
/wd4324
/WX
/D_CRT_SECURE_NO_WARNINGS
${WPILIB_TARGET_WARNINGS}
set(WARNING_FLAGS
/wd4146
/wd4244
/wd4251
/wd4267
/wd4324
/D_CRT_SECURE_NO_WARNINGS
${WPILIB_TARGET_WARNINGS}
)
if(NOT NO_WERROR)
set(WARNING_FLAGS ${WARNING_FLAGS} /WX)
endif()
target_compile_options(${target} PRIVATE ${WARNING_FLAGS})
endif()
# Suppress C++-specific OpenCV warning; C compiler rejects it with an error
@@ -44,7 +46,7 @@ macro(wpilib_target_warnings target)
# Suppress warning "enumeration types with a fixed underlying type are a
# Clang extension"
if(APPLE)
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,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,8 +1,99 @@
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")
java_library(
WIN_SRCS = glob([
"src/main/native/windows/**/*.cpp",
"src/main/native/windows/**/*.h",
])
LINUX_SRCS = glob([
"src/main/native/linux/**/*.cpp",
"src/main/native/linux/**/*.h",
])
MAC_SRCS = glob(["src/main/native/osx/**/*.cpp"])
filegroup(
name = "native-srcs",
srcs = select({
"@bazel_tools//src/conditions:darwin": MAC_SRCS,
"@bazel_tools//src/conditions:windows": WIN_SRCS,
"@rules_bzlmodrio_toolchains//constraints/combined:is_linux": LINUX_SRCS,
}),
)
wpilib_objc_library(
name = "cscore-mac",
srcs = glob([
"src/main/native/objcpp/**/*.mm",
"src/main/native/cpp/*.h",
]),
hdrs = glob([
"src/main/native/include/**/*",
"src/main/native/objcpp/**/*.h",
]),
include_arc = False,
includes = [
"src/main/native/cpp",
"src/main/native/include",
"src/main/native/objcpp",
],
sdk_frameworks = [
"CoreFoundation",
"AVFoundation",
"Foundation",
"CoreMedia",
"CoreVideo",
"IOKit",
],
deps = [
"//wpinet:wpinet.static",
"//wpiutil:wpiutil.static",
"@bzlmodrio-opencv//libraries/cpp/opencv",
],
)
wpilib_cc_library(
name = "cscore.static",
srcs = [":native-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 = [
"//wpinet:wpinet.static",
"//wpiutil:wpiutil.static",
"@bzlmodrio-opencv//libraries/cpp/opencv",
] + select({
"@bazel_tools//src/conditions:darwin": [":cscore-mac"],
"//conditions:default": [],
}),
)
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",
@@ -10,6 +101,33 @@ java_library(
],
)
cc_test(
name = "cscore-cpp-test",
size = "small",
srcs = glob(["src/test/native/**"]),
deps = [
":cscore.static",
"//thirdparty/googletest:googletest.static",
],
)
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"],
deps = [
":cscore.static",
],
)
java_binary(
name = "DevMain-Java",
srcs = ["src/dev/java/edu/wpi/first/cscore/DevMain.java"],
@@ -19,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 {
@@ -185,8 +180,8 @@ model {
lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
lib project: ':wpigui', library: 'wpigui', linkage: 'static'
lib library: 'cscore', linkage: 'shared'
lib project: ':thirdparty:imgui_suite', library: 'imgui', linkage: 'static'
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
lib project: ':thirdparty:imgui_suite', library: 'imguiSuite', linkage: 'static'
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

@@ -6,6 +6,7 @@ package edu.wpi.first.cscore;
import edu.wpi.first.util.PixelFormat;
import edu.wpi.first.util.RawFrame;
import edu.wpi.first.util.TimestampSource;
import java.nio.ByteBuffer;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
@@ -44,7 +45,7 @@ public class CvSink extends ImageSink {
* Create a sink for accepting OpenCV images. grabFrame() must be called on the created sink to
* get each new image.
*
* @param name Source name (arbitrary unique identifier)
* @param name Sink name (arbitrary unique identifier)
* @param pixelFormat Source pixel format
*/
public CvSink(String name, PixelFormat pixelFormat) {
@@ -220,4 +221,22 @@ public class CvSink extends ImageSink {
}
return rv;
}
/**
* Get the last time a frame was grabbed. This uses the same time base as wpi::Now().
*
* @return Time in 1 us increments.
*/
public long getLastFrameTime() {
return m_frame.getTimestamp();
}
/**
* Get the time source for the timestamp the last frame was grabbed at.
*
* @return Time source
*/
public TimestampSource getLastFrameTimeSource() {
return m_frame.getTimestampSource();
}
}

View File

@@ -16,18 +16,22 @@
using namespace cs;
Frame::Frame(SourceImpl& source, std::string_view error, Time time)
Frame::Frame(SourceImpl& source, std::string_view error, Time time,
WPI_TimestampSource timeSrc)
: m_impl{source.AllocFrameImpl().release()} {
m_impl->refcount = 1;
m_impl->error = error;
m_impl->time = time;
m_impl->timeSource = timeSrc;
}
Frame::Frame(SourceImpl& source, std::unique_ptr<Image> image, Time time)
Frame::Frame(SourceImpl& source, std::unique_ptr<Image> image, Time time,
WPI_TimestampSource timeSrc)
: m_impl{source.AllocFrameImpl().release()} {
m_impl->refcount = 1;
m_impl->error.resize(0);
m_impl->time = time;
m_impl->timeSource = timeSrc;
m_impl->images.push_back(image.release());
}

View File

@@ -39,6 +39,7 @@ class Frame {
wpi::recursive_mutex mutex;
std::atomic_int refcount{0};
Time time{0};
WPI_TimestampSource timeSource{WPI_TIMESRC_UNKNOWN};
SourceImpl& source;
std::string error;
wpi::SmallVector<Image*, 4> images;
@@ -48,9 +49,11 @@ class Frame {
public:
Frame() noexcept = default;
Frame(SourceImpl& source, std::string_view error, Time time);
Frame(SourceImpl& source, std::string_view error, Time time,
WPI_TimestampSource timeSrc);
Frame(SourceImpl& source, std::unique_ptr<Image> image, Time time);
Frame(SourceImpl& source, std::unique_ptr<Image> image, Time time,
WPI_TimestampSource timeSrc);
Frame(const Frame& frame) noexcept : m_impl{frame.m_impl} {
if (m_impl) {
@@ -75,6 +78,9 @@ class Frame {
}
Time GetTime() const { return m_impl ? m_impl->time : 0; }
WPI_TimestampSource GetTimeSource() const {
return m_impl ? m_impl->timeSource : WPI_TIMESRC_UNKNOWN;
}
std::string_view GetError() const {
if (!m_impl) {

View File

@@ -50,7 +50,7 @@ inline void NamedLog(wpi::Logger& logger, unsigned int level, const char* file,
#define SLOG(level, format, ...) \
NamedLog(m_logger, level, __FILE__, __LINE__, GetName(), \
FMT_STRING(format) __VA_OPT__(, ) __VA_ARGS__)
format __VA_OPT__(, ) __VA_ARGS__)
#define SERROR(format, ...) \
SLOG(::wpi::WPI_LOG_ERROR, format __VA_OPT__(, ) __VA_ARGS__)

View File

@@ -63,6 +63,11 @@ uint64_t RawSinkImpl::GrabFrame(WPI_RawFrame& image) {
}
uint64_t RawSinkImpl::GrabFrame(WPI_RawFrame& image, double timeout) {
return GrabFrame(image, timeout, 0);
}
uint64_t RawSinkImpl::GrabFrame(WPI_RawFrame& image, double timeout,
uint64_t lastFrameTime) {
SetEnabled(true);
auto source = GetSource();
@@ -72,7 +77,7 @@ uint64_t RawSinkImpl::GrabFrame(WPI_RawFrame& image, double timeout) {
return 0;
}
auto frame = source->GetNextFrame(timeout); // blocks
auto frame = source->GetNextFrame(timeout, lastFrameTime); // blocks
if (!frame) {
// Bad frame; sleep for 20 ms so we don't consume all processor time.
std::this_thread::sleep_for(std::chrono::milliseconds(20));
@@ -115,6 +120,8 @@ uint64_t RawSinkImpl::GrabFrameImpl(WPI_RawFrame& rawFrame,
rawFrame.pixelFormat = newImage->pixelFormat;
rawFrame.size = newImage->size();
std::copy(newImage->data(), newImage->data() + rawFrame.size, rawFrame.data);
rawFrame.timestamp = incomingFrame.GetTime();
rawFrame.timestampSrc = incomingFrame.GetTimeSource();
return incomingFrame.GetTime();
}
@@ -183,6 +190,18 @@ uint64_t GrabSinkFrameTimeout(CS_Sink sink, WPI_RawFrame& image, double timeout,
return static_cast<RawSinkImpl&>(*data->sink).GrabFrame(image, timeout);
}
uint64_t GrabSinkFrameTimeoutLastTime(CS_Sink sink, WPI_RawFrame& image,
double timeout, uint64_t lastFrameTime,
CS_Status* status) {
auto data = Instance::GetInstance().GetSink(sink);
if (!data || (data->kind & SinkMask) == 0) {
*status = CS_INVALID_HANDLE;
return 0;
}
return static_cast<RawSinkImpl&>(*data->sink)
.GrabFrame(image, timeout, lastFrameTime);
}
} // namespace cs
extern "C" {
@@ -209,4 +228,13 @@ uint64_t CS_GrabRawSinkFrameTimeout(CS_Sink sink, struct WPI_RawFrame* image,
return cs::GrabSinkFrameTimeout(sink, *image, timeout, status);
}
uint64_t CS_GrabRawSinkFrameTimeoutWithFrameTime(CS_Sink sink,
struct WPI_RawFrame* image,
double timeout,
uint64_t lastFrameTime,
CS_Status* status) {
return cs::GrabSinkFrameTimeoutLastTime(sink, *image, timeout, lastFrameTime,
status);
}
} // extern "C"

View File

@@ -34,10 +34,15 @@ class RawSinkImpl : public SinkImpl {
uint64_t GrabFrame(WPI_RawFrame& frame);
uint64_t GrabFrame(WPI_RawFrame& frame, double timeout);
// Wait for a frame with a time other than lastFrameTime
uint64_t GrabFrame(WPI_RawFrame& frame, double timeout,
uint64_t lastFrameTime);
private:
void ThreadMain();
// Copies the image from incomingFrame into rawFrame, converting where
// necessary to the resolution of rawFrame
uint64_t GrabFrameImpl(WPI_RawFrame& rawFrame, Frame& incomingFrame);
std::atomic_bool m_active; // set to false to terminate threads

View File

@@ -29,7 +29,7 @@ SourceImpl::SourceImpl(std::string_view name, wpi::Logger& logger,
m_notifier(notifier),
m_telemetry(telemetry),
m_name{name} {
m_frame = Frame{*this, std::string_view{}, 0};
m_frame = Frame{*this, std::string_view{}, 0, WPI_TIMESRC_UNKNOWN};
}
SourceImpl::~SourceImpl() {
@@ -84,13 +84,19 @@ Frame SourceImpl::GetNextFrame() {
return m_frame;
}
Frame SourceImpl::GetNextFrame(double timeout) {
Frame SourceImpl::GetNextFrame(double timeout, Frame::Time lastFrameTime) {
std::unique_lock lock{m_frameMutex};
auto oldTime = m_frame.GetTime();
if (lastFrameTime == 0) {
lastFrameTime = m_frame.GetTime();
}
// Wait unitl m_frame has a timestamp other than lastFrameTime
if (!m_frameCv.wait_for(
lock, std::chrono::milliseconds(static_cast<int>(timeout * 1000)),
[=, this] { return m_frame.GetTime() != oldTime; })) {
m_frame = Frame{*this, "timed out getting frame", wpi::Now()};
[=, this] { return m_frame.GetTime() != lastFrameTime; })) {
m_frame = Frame{*this, "timed out getting frame", wpi::Now(),
WPI_TIMESRC_UNKNOWN};
}
return m_frame;
}
@@ -98,7 +104,7 @@ Frame SourceImpl::GetNextFrame(double timeout) {
void SourceImpl::Wakeup() {
{
std::scoped_lock lock{m_frameMutex};
m_frame = Frame{*this, std::string_view{}, 0};
m_frame = Frame{*this, std::string_view{}, 0, WPI_TIMESRC_UNKNOWN};
}
m_frameCv.notify_all();
}
@@ -458,7 +464,8 @@ std::unique_ptr<Image> SourceImpl::AllocImage(
}
void SourceImpl::PutFrame(VideoMode::PixelFormat pixelFormat, int width,
int height, std::string_view data, Frame::Time time) {
int height, std::string_view data, Frame::Time time,
WPI_TimestampSource timeSrc) {
if (pixelFormat == VideoMode::PixelFormat::kBGRA) {
// Write BGRA as BGR to save a copy
auto image =
@@ -475,10 +482,11 @@ void SourceImpl::PutFrame(VideoMode::PixelFormat pixelFormat, int width,
fmt::ptr(data.data()), data.size());
std::memcpy(image->data(), data.data(), data.size());
PutFrame(std::move(image), time);
PutFrame(std::move(image), time, timeSrc);
}
void SourceImpl::PutFrame(std::unique_ptr<Image> image, Frame::Time time) {
void SourceImpl::PutFrame(std::unique_ptr<Image> image, Frame::Time time,
WPI_TimestampSource timeSrc) {
// Update telemetry
m_telemetry.RecordSourceFrames(*this, 1);
m_telemetry.RecordSourceBytes(*this, static_cast<int>(image->size()));
@@ -486,7 +494,7 @@ void SourceImpl::PutFrame(std::unique_ptr<Image> image, Frame::Time time) {
// Update frame
{
std::scoped_lock lock{m_frameMutex};
m_frame = Frame{*this, std::move(image), time};
m_frame = Frame{*this, std::move(image), time, timeSrc};
}
// Signal listeners
@@ -497,7 +505,7 @@ void SourceImpl::PutError(std::string_view msg, Frame::Time time) {
// Update frame
{
std::scoped_lock lock{m_frameMutex};
m_frame = Frame{*this, msg, time};
m_frame = Frame{*this, msg, time, WPI_TIMESRC_UNKNOWN};
}
// Signal listeners

View File

@@ -13,6 +13,7 @@
#include <vector>
#include <wpi/Logger.h>
#include <wpi/RawFrame.h>
#include <wpi/condition_variable.h>
#include <wpi/json_fwd.h>
#include <wpi/mutex.h>
@@ -98,7 +99,8 @@ class SourceImpl : public PropertyContainer {
// Blocking function that waits for the next frame and returns it (with
// timeout in seconds). If timeout expires, returns empty frame.
Frame GetNextFrame(double timeout);
// If lastFrameTime==0, uses m_frame.GetTime() for lastFrameTime
Frame GetNextFrame(double timeout, Frame::Time lastFrameTime = 0);
// Force a wakeup of all GetNextFrame() callers by sending an empty frame.
void Wakeup();
@@ -140,8 +142,10 @@ class SourceImpl : public PropertyContainer {
std::string_view valueStr) override;
void PutFrame(VideoMode::PixelFormat pixelFormat, int width, int height,
std::string_view data, Frame::Time time);
void PutFrame(std::unique_ptr<Image> image, Frame::Time time);
std::string_view data, Frame::Time time,
WPI_TimestampSource timeSrc = WPI_TIMESRC_FRAME_DEQUEUE);
void PutFrame(std::unique_ptr<Image> image, Frame::Time time,
WPI_TimestampSource timeSrc = WPI_TIMESRC_FRAME_DEQUEUE);
void PutError(std::string_view msg, Frame::Time time);
// Notification functions for corresponding atomics

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

@@ -8,6 +8,7 @@
#include <functional>
#include <opencv2/core/mat.hpp>
#include <wpi/RawFrame.h>
#include "cscore_oo.h"
#include "cscore_raw.h"
@@ -151,6 +152,44 @@ class CvSink : public ImageSink {
[[nodiscard]]
uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image);
/**
* Wait for the next frame and get the image.
* Times out (returning 0) after timeout seconds.
* The provided image will have the pixelFormat this class was constructed
* with. The data is backed by data in the CvSink. It will be invalidated by
* any grabFrame*() call on the sink.
*
* <p>If lastFrameTime is provided and non-zero, the sink will fill image with
* the first frame from the source that is not equal to lastFrameTime. If
* lastFrameTime is zero, the time of the current frame owned by the CvSource
* is used, and this function will block until the connected CvSource provides
* a new frame.
*
* @return Frame time, or 0 on error (call GetError() to obtain the error
* message); the frame time is in the same time base as wpi::Now(),
* and is in 1 us increments.
*/
[[nodiscard]]
uint64_t GrabFrameDirectLastTime(cv::Mat& image, uint64_t lastFrameTime,
double timeout = 0.225);
/**
* Get the last time a frame was grabbed. This uses the same time base as
* wpi::Now().
*
* @return Time in 1 us increments.
*/
[[nodiscard]]
uint64_t LastFrameTime();
/**
* Get the time source for the timestamp the last frame was grabbed at.
*
* @return Time source
*/
[[nodiscard]]
WPI_TimestampSource LastFrameTimeSource();
private:
constexpr int GetCvFormat(WPI_PixelFormat pixelFormat);
@@ -365,6 +404,33 @@ inline uint64_t CvSink::GrabFrameNoTimeoutDirect(cv::Mat& image) {
return timestamp;
}
inline uint64_t CvSink::GrabFrameDirectLastTime(cv::Mat& image,
uint64_t lastFrameTime,
double timeout) {
rawFrame.height = 0;
rawFrame.width = 0;
rawFrame.stride = 0;
rawFrame.pixelFormat = pixelFormat;
auto timestamp = GrabSinkFrameTimeoutLastTime(m_handle, rawFrame, timeout,
lastFrameTime, &m_status);
if (m_status != CS_OK) {
return 0;
}
image =
cv::Mat{rawFrame.height, rawFrame.width,
GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
rawFrame.data, static_cast<size_t>(rawFrame.stride)};
return timestamp;
}
inline uint64_t CvSink::LastFrameTime() {
return rawFrame.timestamp;
}
inline WPI_TimestampSource CvSink::LastFrameTimeSource() {
return static_cast<WPI_TimestampSource>(rawFrame.timestampSrc);
}
} // namespace cs
#endif // CSCORE_CSCORE_CV_H_

View File

@@ -27,6 +27,11 @@ uint64_t CS_GrabRawSinkFrame(CS_Sink sink, struct WPI_RawFrame* rawImage,
CS_Status* status);
uint64_t CS_GrabRawSinkFrameTimeout(CS_Sink sink, struct WPI_RawFrame* rawImage,
double timeout, CS_Status* status);
uint64_t CS_GrabRawSinkFrameTimeoutWithFrameTime(CS_Sink sink,
struct WPI_RawFrame* rawImage,
double timeout,
uint64_t lastFrameTime,
CS_Status* status);
CS_Sink CS_CreateRawSink(const struct WPI_String* name, CS_Bool isCv,
CS_Status* status);
@@ -67,6 +72,9 @@ void PutSourceFrame(CS_Source source, const WPI_RawFrame& image,
uint64_t GrabSinkFrame(CS_Sink sink, WPI_RawFrame& image, CS_Status* status);
uint64_t GrabSinkFrameTimeout(CS_Sink sink, WPI_RawFrame& image, double timeout,
CS_Status* status);
uint64_t GrabSinkFrameTimeoutLastTime(CS_Sink sink, WPI_RawFrame& image,
double timeout, uint64_t lastFrameTime,
CS_Status* status);
/**
* A source for user code to provide video frames as raw bytes.
@@ -163,6 +171,24 @@ class RawSink : public ImageSink {
*/
[[nodiscard]]
uint64_t GrabFrameNoTimeout(wpi::RawFrame& image) const;
/**
* Wait for the next frame and get the image. May block forever.
* The provided image will have three 8-bit channels stored in BGR order.
*
* <p>If lastFrameTime is provided and non-zero, the sink will fill image with
* the first frame from the source that is not equal to lastFrameTime. If
* lastFrameTime is zero, the time of the current frame owned by the CvSource
* is used, and this function will block until the connected CvSource provides
* a new frame.
*
* @return Frame time, or 0 on error (call GetError() to obtain the error
* message); the frame time is in the same time base as wpi::Now(),
* and is in 1 us increments.
*/
[[nodiscard]]
uint64_t GrabFrameLastTime(wpi::RawFrame& image, uint64_t lastFrameTime,
double timeout = 0.225) const;
};
inline RawSource::RawSource(std::string_view name, const VideoMode& mode) {
@@ -199,6 +225,14 @@ inline uint64_t RawSink::GrabFrameNoTimeout(wpi::RawFrame& image) const {
m_status = 0;
return GrabSinkFrame(m_handle, image, &m_status);
}
inline uint64_t RawSink::GrabFrameLastTime(wpi::RawFrame& image,
uint64_t lastFrameTime,
double timeout) const {
m_status = 0;
return GrabSinkFrameTimeoutLastTime(m_handle, image, timeout, lastFrameTime,
&m_status);
}
/** @} */
} // namespace cs

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

@@ -555,8 +555,51 @@ void UsbCameraImpl::CameraThreadMain() {
good = false;
}
if (good) {
Frame::Time frameTime{wpi::Now()};
WPI_TimestampSource timeSource{WPI_TIMESRC_FRAME_DEQUEUE};
// check the timestamp time
auto tsFlags = buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK;
SDEBUG4("Flags {}", tsFlags);
if (tsFlags == V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN) {
SDEBUG4("Got unknown time for frame - default to wpi::Now");
} else if (tsFlags == V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) {
SDEBUG4("Got valid monotonic time for frame");
// we can't go directly to frametime, since the rest of cscore
// expects us to use wpi::Now, which is in an arbitrary timebase
// (see timestamp.cpp). Best I can do is (approximately) translate
// between timebases
// grab current time in the same timebase as buf.timestamp
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
int64_t nowTime = {ts.tv_sec * 1'000'000 + ts.tv_nsec / 1000};
int64_t bufTime = {buf.timestamp.tv_sec * 1'000'000 +
buf.timestamp.tv_usec};
// And offset frameTime by the latency
int64_t offset{nowTime - bufTime};
frameTime -= offset;
// Figure out the timestamp's source
int tsrcFlags = buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
if (tsrcFlags == V4L2_BUF_FLAG_TSTAMP_SRC_EOF) {
timeSource = WPI_TIMESRC_V4L_EOF;
} else if (tsrcFlags == V4L2_BUF_FLAG_TSTAMP_SRC_SOE) {
timeSource = WPI_TIMESRC_V4L_SOE;
} else {
timeSource = WPI_TIMESRC_UNKNOWN;
}
SDEBUG4("Frame was {} uS old, flags {}, source {}", offset,
tsrcFlags, static_cast<int>(timeSource));
} else {
// Can't do anything if we can't access the clock, leave default
}
} else if (tsFlags == V4L2_BUF_FLAG_TIMESTAMP_COPY) {
SDEBUG4("Got valid copy time for frame - default to wpi::Now");
}
PutFrame(static_cast<VideoMode::PixelFormat>(m_mode.pixelFormat),
width, height, image, wpi::Now()); // TODO: time
width, height, image, frameTime, timeSource);
}
}

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,
@@ -29,7 +31,7 @@ inline void NamedLog(UsbCameraImplObjc* objc, unsigned int level,
#define OBJCLOG(level, format, ...) \
NamedLog(self, level, __FILE__, __LINE__, \
FMT_STRING(format) __VA_OPT__(, ) __VA_ARGS__)
format __VA_OPT__(, ) __VA_ARGS__)
#define OBJCERROR(format, ...) \
OBJCLOG(::wpi::WPI_LOG_ERROR, format __VA_OPT__(, ) __VA_ARGS__)
@@ -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

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