Squashed 'third_party/allwpilib/' changes from f1a82828fe..ce550705d7
ce550705d7 [ntcore] Fix client "received unknown id -1" (#6186)
3989617bde [ntcore] NetworkTable::GetStruct: Add I template param (#6183)
f1836e1321 [fieldImages] Fix 2024 field json (#6179)
d05f179a9a [build] Fix running apriltagsvision Java example (#6173)
b1b03bed85 [wpilib] Update MotorControllerGroup deprecation message (#6171)
fa63fbf446 LICENSE.md: Bump year to 2024 (#6169)
4809f3d0fc [apriltag] Add 2024 AprilTag locations (#6168)
dd90965362 [wpiutil] Fix RawFrame.setInfo() NPE (#6167)
8659372d08 [fieldImages] Add 2024 field image (#6166)
a2e4d0b15d [docs] Fix docs for SysID routine (#6164)
0a46a3a618 [wpilib] Make ADXL345 default I2C address public (#6163)
7c26bc70ab [sysid] Load DataLog files directly for analysis (#6103)
f94e3d81b9 [docs] Fix SysId routine JavaDoc warnings (#6159)
6bed82a18e [wpilibc] Clean up C++ SysId routine (#6160)
4595f84719 [wpilib] Report LiveWindow-enabled-in-test (#6158)
707cb06105 [wpilib] Add SysIdRoutine logging utility and command factory (#6033)
3e40b9e5da [wpilib] Correct SmartDashboard usage reporting (#6157)
106518c3f8 [docs] Fix wpilibj JavaDoc warnings (#6154)
19cb2a8eb4 [wpilibj] Make class variables private to match C++ (#6153)
13f4460e00 [docs] Add missing docs to enum fields (NFC) (#6150)
4210f5635d [docs] Fix warnings about undocumented default constructors (#6151)
0f060afb55 [ntcore] Disable WebSocket fragmentation (#6149)
f29a7d2e50 [docs] Add missing JavaDocs (#6146)
6e58db398d [commands] Make Java fields private (#6148)
4ac0720385 [build] Clean up CMake files (#6141)
44db3e0ac0 [sysid] Make constexpr variables outside class scope inline (#6145)
73c7d87db7 [glass] NTStringChooser: Properly set retained (#6144)
25636b712f [build] Remove unnecessary native dependencies in wpilibjExamples (#6143)
01fb98baaa [docs] Add Missing JNI docs from C++ (NFC) (#6139)
5c424248c4 [wpilibj] Remove unused AnalogTriggerException (#6142)
c486972c55 [wpimath] Make ExponentialProfile.State mutable (#6138)
783acb9b72 [wpilibj] Store long preferences as integers (#6136)
99ab836894 [wpiutil] Add missing JavaDocs (NFC) (#6132)
ad0859a8c9 [docs] Add missing JavaDocs (#6125)
5579219716 [docs] Exclude quickbuf files and proto/struct packages from doclint (#6128)
98f06911c7 [sysid] Use eigenvector component instead of eigenvalue for fit quality check (#6131)
e1d49b975c [wpimath] Add LinearFilter reset() overload to initialize input and output buffers (#6133)
8a0bf2b7a4 [hal] Add CANAPITypes to java (#6121)
91d8837c11 [wpilib] Make protected fields in accelerometers/gyros private (#6134)
e7c9f27683 [wpilib] Add functional interface equivalents to MotorController (#6053)
8aca706217 [glass] Add type information to SmartDashboard menu (#6117)
7d3e4ddba9 [docs] Add warning about using user button to docs (NFC) (#6129)
ec3cb3dcba [build] Disable clang-tidy warning about test case names (#6127)
495585b25d [examples] Update april tag family to 36h11 (#6126)
f9aabc5ab2 [wpilib] Throw early when EventLoop is modified while running (#6115)
c16946c0ec [hal] Add CANJNI docs (NFC) (#6120)
b7f4eb2811 [doc] Update maven artifacts for units and apriltags (NFC) (#6123)
f419a62b38 [doc] Update maintainers.md (NFC) (#6124)
938bf45fd9 [wpiutil] Remove type param from ProtobufSerializable and StructSerializable (#6122)
c34debe012 [docs] Link to external OpenCV docs (#6119)
07183765de [hal] Fix formatting of HAL_ENUM enums (NFC) (#6114)
af46034b7f [wpilib] Document only first party controllers are guaranteed to have correct mapping (#6112)
636ef58d94 [hal] Properly error check readCANStreamSession (#6108)
cc631d2a69 [build] Fix generated source set location in the HAL (#6113)
09f76b32c2 [wpimath] Compile with UTF-8 encoding (#6111)
47c5fd8620 [sysid] Check data quality before OLS (#6110)
24a76be694 [hal] Add method to detect if the CAN Stream has overflowed (#6105)
9333951736 [hal] Allocate CANStreamMessage in JNI if null (#6107)
6a2d3c30a6 [wpiutil] Struct: Add info template parameter pack (#6086)
e07de37e64 [commands] Mark ParallelDeadlineGroup.setDeadline() final (#6102)
141241d2d6 [wpilib] Fix usage reporting for static classes (#6090)
f2c2bab7dc [sysid] Fix adjusted R² calculation (#6101)
5659038443 [wpiutil,cscore,apriltag] Fix RawFrame (#6098)
8aeee03626 [commands] Improve error message when composing commands twice in same composition (#6091)
55508706ff [wpiutil,cscore] Move VideoMode.PixelFormat to wpiutil (#6097)
ab78b930e9 [wpilib] ADIS16470: Add access to all 3 axes (#6074)
795d4be9fd [wpilib] Fix precision issue in Color round-and-clamp (#6100)
7aa9ad44b8 [commands] Deprecate C++ TransferOwnership() (#6095)
92c81d0791 [ci] Update pregenerate workflow to actions/checkout@v4 (#6094)
1ce617be07 [ci] Update artifact actions to v4 (#6092)
2441b57156 [wpilib] Add PWMSparkFlex MotorController (#6089)
21d1972d7a [wpiutil] DataLog: Ensure file is written on shutdown (#6087)
c29e8c66cf [wpiutil] DataLog: Fix UB in AppendImpl (#6088)
ab309e34ef [glass] Fix order of loading window settings (#6056)
22a322c9f3 [wpimath] Report error on negative PID gains (#6055)
1dba26c937 [wpilib] Add method to get breaker fault at a specific channel in PowerDistribution[Sticky]Faults (#5521)
ef1cb3f41e [commands] Fix compose-while-scheduled issue and test all compositions (#5581)
aeb1a4aa33 [wpiutil] Add serializable marker interfaces (#6060)
c1178d5add [wpilib] Add StadiaController and command wrapper (#6083)
4e4a468d4d [wpimath] Make feedforward classes throw exceptions for negative Kv or Ka (#6084)
d1793f077d [build] cmake: Add NO_WERROR option to disable -Werror (#6071)
43fb6e9f87 [glass] Add Profiled PID controller support & IZone Support (#5959)
bcef6c5398 [apriltag] Fix Java generation functions (#6063)
4059e0cd9f [hal,wpilib] Add function to control "Radio" LED (#6073)
0b2cfb3abc [dlt] Change datalogtool default folder to logs folder (#6079)
df5e439b0c [wpilib] PS4Controller: enable usage reporting (#6081)
0ff7478968 [cscore] Fix RawFrame class not being loaded in JNI (#6077)
6f23d32fe1 [wpilib] AddressableLED: Update warning about single driver (NFC) (#6069)
35a1c52788 [build] Upgrade quickbuf to 1.3.3 (#6072)
e4e2bafdb1 [sysid] Document timestamp units (#6065)
3d201c71f7 [ntcore] Fix overlapping subscriber handling (#6067)
f02984159f [glass] Check for null entries when updating struct/proto (#6059)
a004c9e05f [commands] SubsystemBase: allow setting name in constructor (#6052)
0b4c6a1546 [wpimath] Add more docs to SimulatedAnnealing (NFC) (#6054)
ab15dae887 [wpilib] ArcadeDrive: Fix max output handling (#6051)
9599c1f56f [hal] Add usage reporting ids from 2024v2 image (#6041)
f87c64af8a [wpimath] MecanumDriveWheelSpeeds: Fix desaturate() (#6040)
8798700cec [wpilibcExamples] Add inline specifier to constexpr constants (#6049)
85c9ae6eff [wpilib] Fix PS5 Controller mappings (#6050)
7c8b7a97ad [wpiutil] Zero out roborio system timestamp (#6042)
d9b504bc84 [wpilib] DataLogManager: Change sim location to logs subdir (#6039)
906b810136 [build] cmake: Fix ntcore generated header install (#6038)
56e5b404d1 Update to final 2024 V2 image (#6034)
8723ee5c39 [ntcore] Add cached topic property (#5494)
192a28af47 Fix JDK 21 warnings (#6028)
d40bdd70ba [build] Upgrade to spotbugs Gradle plugin 6.0.2 (#6027)
7bfadf32e5 [wpilibj] Joystick: make remainder of get axis methods final (#6024)
a770110438 [commands] CommandCompositionError: Include stacktrace of original composition (#5984)
54a55b8b53 [wpiutil,hal] Update image; init Rio Now() HMB with a FPGA session (#6016)
7d4e515a6b [wpimath] Simplify calculation of C for DARE precondition (#6022)
5200316c14 [ntcore] Update transmit period on topic add/remove (#6021)
ddf79a25d4 [wpiunits] Overload Measure.per(Time) to return Measure<Velocity> (#6018)
a71adef316 [wpiutil] Clean up circular_buffer iterator syntax (#6020)
39a0bf4b98 [examples] Call resetOdometry() when controller command is executed (#5905)
f5fc101fda [build] cmake: Export jars and clean up jar installs (#6014)
38bf024c96 [build] Update to Gradle 8.5 (#6007)
9d11544c18 [wpimath] Rotate traveling salesman solution so input and solution have same initial pose (#6015)
28deba20f5 [wpimath] Commit generated quickbuf Java files (#5994)
c2971c0bb3 [build] cmake: Export apriltag and wpimath (#6012)
41cfc961e4 gitattributes: Add linguist-generated locations (#6004)
14c3ade155 [wpimath] Struct cleanup (#6011)
90757b9e90 [wpilib] Make Color::HexString() constexpr (#5985)
2676b77873 Fix compilation issues that occur when building with bazel (#6008)
d32c10487c [examples] Update C++ examples to use CommandPtr (#5988)
9bc5fcf886 [build] cmake: Default WITH_JAVA_SOURCE to WITH_JAVA (#6005)
d431abba3b [upstream_utils] Fix GCEM namespace usage and add hypot(x, y, z) (#6002)
2bb1409b82 Clean up Java style (#5990)
66172ab288 Remove submodule (#6003)
e8f8c0ceb0 [upstream_utils] Update to latest Eigen HEAD (#5996)
890992a849 [hal] Commit generated usage reporting files (#5993)
a583ca01e1 [wpiutil] Change Struct to allow non-constexpr implementation (#5992)
ca272de400 [build] Fix Gradle compile_commands.json and clang-tidy warnings (#5977)
76ae090570 [wpiutil] type_traits: Add is_constexpr() (#5997)
5172ab8fd0 [commands] C++ CommandPtr: Prevent null initialization (#5991)
96914143ba [build] Bump native-utils to fix compile_commands.json (#5989)
464e6121ef [ci] Report failed status to Azure on failed tests (#2654)
5dad46cd45 [wpimath] Commit generated files (#5986)
54ab65a63a [ntcore] Commit generated files (#5962)
7ed900ae3a [wpilib] Add hex string constructor to Color and Color8Bit (#5063)
74b85b76a9 [wpimath] Make gcem call std functions if not constant-evaluated (#5983)
30816111db [wpimath] Fix TimeInterpolatableBuffer crash (#5972)
5cc923de33 [wpilib] DataLogManager: Use logs subdirectory on USB drives (#5975)
1144115da0 [commands] Add GetName to Subsystem, use in Scheduler tracer epochs (#5836)
ac7d726ac3 [wpimath] Add simulated annealing (#5961)
e09be72ee0 [wpimath] Remove unused SimpleMatrixUtils class (#5979)
0f9ebe92d9 [wpimath] Add generic circular buffer class to Java (#5969)
9fa28eb07a [ci] Bump actions/checkout to v4 (#5736)
ca684ac207 [hal] Add capability to read power distribution data as a stream (#4983)
51eecef2bd [wpimath] Optimize 2nd derivative of quintic splines (#3292)
4fcf0b25a1 [build] Apply a formatter for CMake files (#5973)
9b8011aa67 [build] Pin wpiformat version (#5982)
e00a0e84c1 [build] cmake: fix protobuf dependency finding for certain distributions (#5981)
23dd591394 [upstream_utils] Remove libuv patch that adjusts whitespace (#5976)
b0719942f0 [wpiutil] Timestamp: Report errors on Rio HMB init failure (#5974)
7bc89c4322 [wpilib] Update getAlliance() docs (NFC) (#5971)
841ea682d1 [upstream_utils] Upgrade to LLVM 17.0.5 (#5970)
a74db52dae [cameraserver] Add getVideo() pixelFormat overload (#5966)
a7eb422662 [build] Update native utils for new compile commands files (#5968)
544b231d4d [sysid] Add missing cassert include (#5967)
31cd015970 [wpimath] Add SysId doc links to LinearSystemId in C++ (NFC) (#5960)
9280054eab Revert "[build] Export wpimath protobuf symbols (#5952)"
2aba97c610 Export pb files from wpimath
c80b2d2017 [build] Export wpimath protobuf symbols (#5952)
3c0652c18a [cscore] Replace CS_PixelFormat with WPI_PixelFormat (#5954)
95716eb0cb [wpiunits] Documentation improvements (#5932)
423fd75fa8 [wpilib] Default LiveWindowEnabledInTest to false (#5950)
dfdea9c992 [wpimath] Make KalmanFilter variant for asymmetric updates (#5951)
ca81ced409 [wpiutil] Move RawFrame to wpiutil; add generation of RawFrame for AprilTags (#5923)
437cc91af5 [cscore] CvSink: Allow specifying output PixelFormat (#5943)
25b7dca46b [build] Remove CMake flat install option (#5944)
bb05e20247 [wpimath] Add protobuf/struct for trivial types (#5935)
35744a036e [wpimath] Move struct/proto classes to separate files (#5918)
80d7ad58ea [build] Declare platform launcher dependency explicitly (#5909)
f8d983b154 [ntcore] Protobuf/Struct: Use atomic_bool instead of atomic_flag (#5946)
4a44210ee3 [ntcore] NetworkTableInstance: Suppress unused lambda capture warning (#5947)
bdc8620d55 [upstream_utils] Fix fmt compilation errors on Windows (#5948)
0ca1e9b5f9 [wpimath] Add basic wpiunits support (#5821)
cc30824409 [ntcore] Increase client meta-topic decoding limit (#5934)
b1fad062f7 [wpilib] Use RKDP in DifferentialDrivetrainSim (#5931)
ead9ae5a69 [build] Add generateProto dependency to test and dev (#5933)
cfbff32185 [wpiutil] timestamp: Fix startup race on Rio (#5930)
7d90d0bcc3 [wpimath] Clean up StateSpaceUtil (#5891)
7755e45aac [build] Add generated protobuf headers to C++ test include path (#5926)
3985c031da [ntcore] ProtobufSubscriber: Fix typos (#5928)
7a87fe4b60 [ntcore] ProtobufSubscriber: Make mutex and msg mutable (#5927)
09f3ed6a5f [commands] Add static Trigger factories for robot mode changes (#5902)
79dd795bc0 [wpimath] Clean up VecBuilder and MatBuilder (#5906)
e117274a67 [wpilib] Change default Rio log dir from /home/lvuser to /home/lvuser/logs (#5899)
a8b80ca256 [upstream_utils] Update to libuv 1.47.0 (#5889)
b3a9c3e96b [build] Bump macOS deployment target to 12 (#5890)
0f8129677b [build] Distribute wpimath protobuf headers (#5925)
d105f9e3e9 [wpiutil] ProtobufBuffer: Fix buffer reallocation (#5924)
c5f2f6a0fb [fieldImages] Fix typo in field images artifact name (#5922)
c1a57e422a [commands] Clean up make_vector.h (#5917)
78ebc6e9ec [wpimath] change G to gearing in LinearSystemId factories (#5834)
9ada181866 [hal] DriverStation.h: Add stddef.h include (#5897)
95fa5ec72f [wpilibc,ntcoreffi] DataLogManager: join on Stop() call (#5910)
b6f2d3cc14 [build] Remove usage of Version.parse (#5911)
cc2cbeb04c [examples] Replace gyro rotation with poseEstimator rotation (#5900)
fa6b171e1c [wpiutil] Suppress protobuf warning false positives on GCC 13 (#5907)
d504639bbe [apriltag] Improve AprilTag docs (#5895)
3a1194be40 Replace static_cast<void>() with [[maybe_unused]] attribute (#5892)
70392cbbcb [build] cmake: Add protobuf dependency to wpiutil-config (#5886)
17c1bd5a83 [ntcore] Use json_fwd (#5881)
e69a9efeba [wpilibcExamples] Match array parameter bounds (#5880)
14dcd0d26f Use char instead of uint8_t for json::parse (#5877)
ec1d261984 [hal] Fix garbage data for match info before DS connection (#5879)
63dbf5c614 [wpiutil] MemoryBuffer: Fix normal read and file type check (#5875)
b2e7be9250 [ntcore] Only datalog meta-topics if specifically requested (#5873)
201a42a3cd [wpimath] Reorder TrapezoidProfile.calculate() arguments (#5874)
04a781b4d7 [apriltag] Add GetTags to C++ version of AprilTagFieldLayout (#5872)
87a8a1ced4 [docs] Exclude eigen and protobuf from doxygen (#5871)
git-subtree-dir: third_party/allwpilib
git-subtree-split: ce550705d7cdab117c0153a202973fc026a81274
Signed-off-by: Maxwell Henderson <mxwhenderson@gmail.com>
Change-Id: Ic8645d0551d62b411b0a816c493f0f33291896a1
diff --git a/wpiutil/CMakeLists.txt b/wpiutil/CMakeLists.txt
index a319293..a3f6f19 100644
--- a/wpiutil/CMakeLists.txt
+++ b/wpiutil/CMakeLists.txt
@@ -6,104 +6,118 @@
include(AddTest)
include(DownloadAndCheck)
-file(GLOB wpiutil_jni_src src/main/native/cpp/jni/WPIUtilJNI.cpp src/main/native/cpp/jni/DataLogJNI.cpp)
+file(
+ GLOB wpiutil_jni_src
+ src/main/native/cpp/jni/WPIUtilJNI.cpp
+ src/main/native/cpp/jni/DataLogJNI.cpp
+)
# Java bindings
-if (WITH_JAVA)
- find_package(Java REQUIRED)
- find_package(JNI REQUIRED)
- include(UseJava)
- set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
+if(WITH_JAVA)
+ find_package(Java REQUIRED)
+ find_package(JNI REQUIRED)
+ include(UseJava)
+ set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
- if(NOT EXISTS "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/jackson/jackson-core-2.15.2.jar")
+ if(NOT EXISTS "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/jackson/jackson-core-2.15.2.jar")
set(BASE_URL "https://search.maven.org/remotecontent?filepath=")
set(JAR_ROOT "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/jackson")
message(STATUS "Downloading Jackson jarfiles...")
download_and_check("${BASE_URL}com/fasterxml/jackson/core/jackson-core/2.15.2/jackson-core-2.15.2.jar"
- "${JAR_ROOT}/jackson-core-2.15.2.jar")
+ "${JAR_ROOT}/jackson-core-2.15.2.jar"
+ )
download_and_check("${BASE_URL}com/fasterxml/jackson/core/jackson-databind/2.15.2/jackson-databind-2.15.2.jar"
- "${JAR_ROOT}/jackson-databind-2.15.2.jar")
+ "${JAR_ROOT}/jackson-databind-2.15.2.jar"
+ )
download_and_check("${BASE_URL}com/fasterxml/jackson/core/jackson-annotations/2.15.2/jackson-annotations-2.15.2.jar"
- "${JAR_ROOT}/jackson-annotations-2.15.2.jar")
+ "${JAR_ROOT}/jackson-annotations-2.15.2.jar"
+ )
message(STATUS "All files downloaded.")
endif()
- file(GLOB JACKSON_JARS
- ${WPILIB_BINARY_DIR}/wpiutil/thirdparty/jackson/*.jar)
+ file(GLOB JACKSON_JARS ${WPILIB_BINARY_DIR}/wpiutil/thirdparty/jackson/*.jar)
- if(NOT EXISTS "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/quickbuf/quickbuf-runtime-1.3.2.jar")
+ if(NOT EXISTS "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/quickbuf/quickbuf-runtime-1.3.3.jar")
set(BASE_URL "https://search.maven.org/remotecontent?filepath=")
set(JAR_ROOT "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/quickbuf")
message(STATUS "Downloading Quickbuf jarfile...")
- file(DOWNLOAD "${BASE_URL}us/hebi/quickbuf/quickbuf-runtime/1.3.2/quickbuf-runtime-1.3.2.jar"
- "${JAR_ROOT}/quickbuf-runtime-1.3.2.jar")
+ file(
+ DOWNLOAD
+ "${BASE_URL}us/hebi/quickbuf/quickbuf-runtime/1.3.3/quickbuf-runtime-1.3.3.jar"
+ "${JAR_ROOT}/quickbuf-runtime-1.3.3.jar"
+ )
message(STATUS "Downloaded.")
- endif()
+ endif()
- file(GLOB QUICKBUF_JAR
- ${WPILIB_BINARY_DIR}/wpiutil/thirdparty/quickbuf/*.jar)
+ file(GLOB QUICKBUF_JAR ${WPILIB_BINARY_DIR}/wpiutil/thirdparty/quickbuf/*.jar)
- set(CMAKE_JAVA_INCLUDE_PATH wpiutil.jar ${JACKSON_JARS} ${QUICKBUF_JAR})
+ set(CMAKE_JAVA_INCLUDE_PATH wpiutil.jar ${JACKSON_JARS} ${QUICKBUF_JAR})
- set(CMAKE_JNI_TARGET true)
+ set(CMAKE_JNI_TARGET true)
- file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
+ file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
- add_jar(wpiutil_jar ${JAVA_SOURCES} INCLUDE_JARS ${JACKSON_JARS} ${QUICKBUF_JAR} OUTPUT_NAME wpiutil GENERATE_NATIVE_HEADERS wpiutil_jni_headers)
+ add_jar(
+ wpiutil_jar
+ ${JAVA_SOURCES}
+ INCLUDE_JARS ${JACKSON_JARS} ${QUICKBUF_JAR}
+ OUTPUT_NAME wpiutil
+ GENERATE_NATIVE_HEADERS wpiutil_jni_headers
+ )
- get_property(WPIUTIL_JAR_FILE TARGET wpiutil_jar PROPERTY JAR_FILE)
- install(FILES ${WPIUTIL_JAR_FILE} DESTINATION "${java_lib_dest}")
+ install_jar(wpiutil_jar DESTINATION ${java_lib_dest})
+ install_jar_exports(TARGETS wpiutil_jar FILE wpiutil_jar.cmake DESTINATION share/wpiutil)
- set_property(TARGET wpiutil_jar PROPERTY FOLDER "java")
+ add_library(wpiutiljni ${wpiutil_jni_src})
+ wpilib_target_warnings(wpiutiljni)
+ target_link_libraries(wpiutiljni PUBLIC wpiutil)
- add_library(wpiutiljni ${wpiutil_jni_src})
- wpilib_target_warnings(wpiutiljni)
- target_link_libraries(wpiutiljni PUBLIC wpiutil)
+ set_property(TARGET wpiutiljni PROPERTY FOLDER "libraries")
- set_property(TARGET wpiutiljni PROPERTY FOLDER "libraries")
+ target_link_libraries(wpiutiljni PRIVATE wpiutil_jni_headers)
+ add_dependencies(wpiutiljni wpiutil_jar)
- target_link_libraries(wpiutiljni PRIVATE wpiutil_jni_headers)
- add_dependencies(wpiutiljni wpiutil_jar)
-
- install(TARGETS wpiutiljni EXPORT wpiutiljni)
-
+ install(TARGETS wpiutiljni EXPORT wpiutiljni)
endif()
-if (WITH_JAVA_SOURCE)
- find_package(Java REQUIRED)
- include(UseJava)
- file(GLOB WPIUTIL_SOURCES src/main/java/edu/wpi/first/util/*.java)
- file(GLOB WPIUTIL_CLEANUP_SOURCES src/main/java/edu/wpi/first/util/cleanup/*.java)
- file(GLOB WPIUTIL_CONCURRENT_SOURCES src/main/java/edu/wpi/first/util/concurrent/*.java)
- file(GLOB WPIUTIL_DATALOG_SOURCES src/main/java/edu/wpi/first/util/datalog/*.java)
- file(GLOB WPIUTIL_FUNCTION_SOURCES src/main/java/edu/wpi/first/util/function/*.java)
- file(GLOB WPIUTIL_SENDABLE_SOURCES src/main/java/edu/wpi/first/util/sendable/*.java)
- add_jar(wpiutil_src_jar
- RESOURCES NAMESPACE "edu/wpi/first/util" ${WPIUTIL_SOURCES}
- NAMESPACE "edu/wpi/first/util/cleanup" ${WPIUTIL_CLEANUP_SOURCES}
- NAMESPACE "edu/wpi/first/util/concurrent" ${WPIUTIL_CONCURRENT_SOURCES}
- NAMESPACE "edu/wpi/first/util/datalog" ${WPIUTIL_DATALOG_SOURCES}
- NAMESPACE "edu/wpi/first/util/function" ${WPIUTIL_FUNCTION_SOURCES}
- NAMESPACE "edu/wpi/first/util/sendable" ${WPIUTIL_SENDABLE_SOURCES}
- OUTPUT_NAME wpiutil-sources)
+if(WITH_JAVA_SOURCE)
+ find_package(Java REQUIRED)
+ include(UseJava)
+ file(GLOB WPIUTIL_SOURCES src/main/java/edu/wpi/first/util/*.java)
+ file(GLOB WPIUTIL_CLEANUP_SOURCES src/main/java/edu/wpi/first/util/cleanup/*.java)
+ file(GLOB WPIUTIL_CONCURRENT_SOURCES src/main/java/edu/wpi/first/util/concurrent/*.java)
+ file(GLOB WPIUTIL_DATALOG_SOURCES src/main/java/edu/wpi/first/util/datalog/*.java)
+ file(GLOB WPIUTIL_FUNCTION_SOURCES src/main/java/edu/wpi/first/util/function/*.java)
+ file(GLOB WPIUTIL_SENDABLE_SOURCES src/main/java/edu/wpi/first/util/sendable/*.java)
+ add_jar(
+ wpiutil_src_jar
+ RESOURCES
+ NAMESPACE "edu/wpi/first/util" ${WPIUTIL_SOURCES}
+ NAMESPACE "edu/wpi/first/util/cleanup" ${WPIUTIL_CLEANUP_SOURCES}
+ NAMESPACE "edu/wpi/first/util/concurrent" ${WPIUTIL_CONCURRENT_SOURCES}
+ NAMESPACE "edu/wpi/first/util/datalog" ${WPIUTIL_DATALOG_SOURCES}
+ NAMESPACE "edu/wpi/first/util/function" ${WPIUTIL_FUNCTION_SOURCES}
+ NAMESPACE "edu/wpi/first/util/sendable" ${WPIUTIL_SENDABLE_SOURCES}
+ OUTPUT_NAME wpiutil-sources
+ )
- get_property(WPIUTIL_SRC_JAR_FILE TARGET wpiutil_src_jar PROPERTY JAR_FILE)
- install(FILES ${WPIUTIL_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
+ get_property(WPIUTIL_SRC_JAR_FILE TARGET wpiutil_src_jar PROPERTY JAR_FILE)
+ install(FILES ${WPIUTIL_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
- set_property(TARGET wpiutil_src_jar PROPERTY FOLDER "java")
+ set_property(TARGET wpiutil_src_jar PROPERTY FOLDER "java")
endif()
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
-if (NOT MSVC AND NOT APPLE AND NOT ANDROID)
+if(NOT MSVC AND NOT APPLE AND NOT ANDROID)
find_library(ATOMIC NAMES atomic libatomic.so.1)
- if (ATOMIC)
+ if(ATOMIC)
message(STATUS "Found libatomic: ${ATOMIC}")
else()
message(STATUS "libatomic not found. If build fails, install libatomic")
@@ -112,10 +126,13 @@
generate_resources(src/main/native/resources generated/main/cpp WPI wpi wpiutil_resources_src)
-file(GLOB_RECURSE wpiutil_native_src src/main/native/cpp/*.cpp
- src/main/native/thirdparty/json/cpp/*.cpp
- src/main/native/thirdparty/llvm/cpp/*.cpp
- src/main/native/thirdparty/mpack/src/*.cpp)
+file(
+ GLOB_RECURSE wpiutil_native_src
+ src/main/native/cpp/*.cpp
+ src/main/native/thirdparty/json/cpp/*.cpp
+ src/main/native/thirdparty/llvm/cpp/*.cpp
+ src/main/native/thirdparty/mpack/src/*.cpp
+)
list(REMOVE_ITEM wpiutil_native_src ${wpiutil_jni_src})
file(GLOB_RECURSE wpiutil_unix_src src/main/native/unix/*.cpp)
file(GLOB_RECURSE wpiutil_linux_src src/main/native/linux/*.cpp)
@@ -131,24 +148,32 @@
set_property(TARGET wpiutil PROPERTY FOLDER "libraries")
target_compile_features(wpiutil PUBLIC cxx_std_20)
-if (MSVC)
- target_compile_options(wpiutil PUBLIC /permissive- /Zc:preprocessor /Zc:throwingNew /MP /bigobj)
+if(MSVC)
+ target_compile_options(
+ wpiutil
+ PUBLIC /permissive- /Zc:preprocessor /Zc:throwingNew /MP /bigobj
+ )
target_compile_definitions(wpiutil PRIVATE -D_CRT_SECURE_NO_WARNINGS)
endif()
wpilib_target_warnings(wpiutil)
target_link_libraries(wpiutil protobuf::libprotobuf Threads::Threads ${CMAKE_DL_LIBS})
-if (ATOMIC)
+if(ATOMIC)
target_link_libraries(wpiutil ${ATOMIC})
endif()
-
-if (NOT USE_SYSTEM_FMTLIB)
+if(NOT USE_SYSTEM_FMTLIB)
target_sources(wpiutil PRIVATE ${fmtlib_native_src})
- install(DIRECTORY src/main/native/thirdparty/fmtlib/include/ DESTINATION "${include_dest}/wpiutil")
- target_include_directories(wpiutil PUBLIC
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/fmtlib/include>
- $<INSTALL_INTERFACE:${include_dest}/wpiutil>)
+ install(
+ DIRECTORY src/main/native/thirdparty/fmtlib/include/
+ DESTINATION "${include_dest}/wpiutil"
+ )
+ target_include_directories(
+ wpiutil
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/fmtlib/include>
+ $<INSTALL_INTERFACE:${include_dest}/wpiutil>
+ )
else()
find_package(fmt CONFIG REQUIRED)
target_link_libraries(wpiutil fmt::fmt)
@@ -161,11 +186,11 @@
endif()
endif()
-if (MSVC)
+if(MSVC)
target_sources(wpiutil PRIVATE ${wpiutil_windows_src})
-else ()
+else()
target_sources(wpiutil PRIVATE ${wpiutil_unix_src})
- if (APPLE)
+ if(APPLE)
target_sources(wpiutil PRIVATE ${wpiutil_macos_src})
else()
target_sources(wpiutil PRIVATE ${wpiutil_linux_src})
@@ -173,47 +198,59 @@
endif()
install(DIRECTORY src/main/native/thirdparty/memory/include/ DESTINATION "${include_dest}/wpiutil")
-target_include_directories(wpiutil PUBLIC
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/memory/include>
- $<INSTALL_INTERFACE:${include_dest}/wpiutil>)
+target_include_directories(
+ wpiutil
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/memory/include>
+ $<INSTALL_INTERFACE:${include_dest}/wpiutil>
+)
install(DIRECTORY src/main/native/thirdparty/json/include/ DESTINATION "${include_dest}/wpiutil")
-target_include_directories(wpiutil PUBLIC
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/json/include>
- $<INSTALL_INTERFACE:${include_dest}/wpiutil>)
+target_include_directories(
+ wpiutil
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/json/include>
+ $<INSTALL_INTERFACE:${include_dest}/wpiutil>
+)
install(DIRECTORY src/main/native/thirdparty/llvm/include/ DESTINATION "${include_dest}/wpiutil")
-target_include_directories(wpiutil PUBLIC
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/llvm/include>
- $<INSTALL_INTERFACE:${include_dest}/wpiutil>)
+target_include_directories(
+ wpiutil
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/llvm/include>
+ $<INSTALL_INTERFACE:${include_dest}/wpiutil>
+)
install(DIRECTORY src/main/native/thirdparty/mpack/include/ DESTINATION "${include_dest}/wpiutil")
-target_include_directories(wpiutil PUBLIC
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/mpack/include>
- $<INSTALL_INTERFACE:${include_dest}/wpiutil>)
+target_include_directories(
+ wpiutil
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/mpack/include>
+ $<INSTALL_INTERFACE:${include_dest}/wpiutil>
+)
install(DIRECTORY src/main/native/thirdparty/sigslot/include/ DESTINATION "${include_dest}/wpiutil")
-target_include_directories(wpiutil PUBLIC
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/sigslot/include>
- $<INSTALL_INTERFACE:${include_dest}/wpiutil>)
+target_include_directories(
+ wpiutil
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/sigslot/include>
+ $<INSTALL_INTERFACE:${include_dest}/wpiutil>
+)
install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/wpiutil")
-target_include_directories(wpiutil PUBLIC
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
- $<INSTALL_INTERFACE:${include_dest}/wpiutil>)
+target_include_directories(
+ wpiutil
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
+ $<INSTALL_INTERFACE:${include_dest}/wpiutil>
+)
install(TARGETS wpiutil EXPORT wpiutil)
-if (WITH_FLAT_INSTALL)
- set (wpiutil_config_dir ${wpilib_dest})
-else()
- set (wpiutil_config_dir share/wpiutil)
-endif()
-
-configure_file(wpiutil-config.cmake.in ${WPILIB_BINARY_DIR}/wpiutil-config.cmake )
-install(FILES ${WPILIB_BINARY_DIR}/wpiutil-config.cmake DESTINATION ${wpiutil_config_dir})
-install(EXPORT wpiutil DESTINATION ${wpiutil_config_dir})
+configure_file(wpiutil-config.cmake.in ${WPILIB_BINARY_DIR}/wpiutil-config.cmake)
+install(FILES ${WPILIB_BINARY_DIR}/wpiutil-config.cmake DESTINATION share/wpiutil)
+install(EXPORT wpiutil DESTINATION share/wpiutil)
subdir_list(wpiutil_examples "${CMAKE_CURRENT_SOURCE_DIR}/examples")
foreach(example ${wpiutil_examples})
@@ -226,7 +263,7 @@
endif()
endforeach()
-if (WITH_TESTS)
+if(WITH_TESTS)
file(GLOB_RECURSE wpiutil_testlib_src src/test/native/include/*.h)
add_library(wpiutil_testlib INTERFACE ${wpiutil_test_src})
target_include_directories(wpiutil_testlib INTERFACE src/test/native/include)
diff --git a/wpiutil/build.gradle b/wpiutil/build.gradle
index ab13092..7e4cc16 100644
--- a/wpiutil/build.gradle
+++ b/wpiutil/build.gradle
@@ -147,12 +147,6 @@
}
}
}
-
- exeSplitSetup = {
- if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
- nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries')
- }
- }
}
def examplesMap = [:];
@@ -252,9 +246,6 @@
targetBuildTypes 'debug'
binaries.all {
lib library: 'wpiutil', linkage: 'shared'
- if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
- nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries')
- }
}
sources {
cpp {
@@ -275,7 +266,7 @@
if (!(it instanceof NativeBinarySpec)) return
if (it.component.name != 'wpiutil' && it.component.name != 'wpiutilBase') return
if (it.targetPlatform.name != nativeUtils.wpi.platforms.roborio) return
- nativeUtils.useRequiredLibrary(it, 'ni_link_libraries')
+ nativeUtils.useRequiredLibrary(it, 'chipobject_headers')
}
}
}
@@ -294,7 +285,7 @@
api "com.fasterxml.jackson.core:jackson-annotations:2.15.2"
api "com.fasterxml.jackson.core:jackson-core:2.15.2"
api "com.fasterxml.jackson.core:jackson-databind:2.15.2"
- api 'us.hebi.quickbuf:quickbuf-runtime:1.3.2'
+ api 'us.hebi.quickbuf:quickbuf-runtime:1.3.3'
printlogImplementation sourceSets.main.output
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/CircularBuffer.java b/wpiutil/src/main/java/edu/wpi/first/util/CircularBuffer.java
index 729c8b1..ff68d4d 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/CircularBuffer.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/CircularBuffer.java
@@ -4,11 +4,13 @@
package edu.wpi.first.util;
-import java.util.Arrays;
-
-/** This is a simple circular buffer so we don't need to "bucket brigade" copy old values. */
-public class CircularBuffer {
- private double[] m_data;
+/**
+ * This is a simple circular buffer so we don't need to "bucket brigade" copy old values.
+ *
+ * @param <T> Buffer element type.
+ */
+public class CircularBuffer<T> {
+ private T[] m_data;
// Index of element at front of buffer
private int m_front;
@@ -19,11 +21,11 @@
/**
* Create a CircularBuffer with the provided size.
*
- * @param size The size of the circular buffer.
+ * @param size Maximum number of buffer elements.
*/
+ @SuppressWarnings("unchecked")
public CircularBuffer(int size) {
- m_data = new double[size];
- Arrays.fill(m_data, 0.0);
+ m_data = (T[]) new Object[size];
}
/**
@@ -40,7 +42,7 @@
*
* @return value at front of buffer
*/
- public double getFirst() {
+ public T getFirst() {
return m_data[m_front];
}
@@ -48,11 +50,14 @@
* Get value at back of buffer.
*
* @return value at back of buffer
+ * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >=
+ * size())
*/
- public double getLast() {
+ @SuppressWarnings("unchecked")
+ public T getLast() {
// If there are no elements in the buffer, do nothing
if (m_length == 0) {
- return 0.0;
+ throw new IndexOutOfBoundsException("getLast() called on an empty container");
}
return m_data[(m_front + m_length - 1) % m_data.length];
@@ -64,7 +69,7 @@
*
* @param value The value to push.
*/
- public void addFirst(double value) {
+ public void addFirst(T value) {
if (m_data.length == 0) {
return;
}
@@ -84,7 +89,7 @@
*
* @param value The value to push.
*/
- public void addLast(double value) {
+ public void addLast(T value) {
if (m_data.length == 0) {
return;
}
@@ -103,14 +108,17 @@
* Pop value at front of buffer.
*
* @return value at front of buffer
+ * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >=
+ * size())
*/
- public double removeFirst() {
+ @SuppressWarnings("unchecked")
+ public T removeFirst() {
// If there are no elements in the buffer, do nothing
if (m_length == 0) {
- return 0.0;
+ throw new IndexOutOfBoundsException("removeFirst() called on an empty container");
}
- double temp = m_data[m_front];
+ T temp = m_data[m_front];
m_front = moduloInc(m_front);
m_length--;
return temp;
@@ -120,11 +128,14 @@
* Pop value at back of buffer.
*
* @return value at back of buffer
+ * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >=
+ * size())
*/
- public double removeLast() {
+ @SuppressWarnings("unchecked")
+ public T removeLast() {
// If there are no elements in the buffer, do nothing
if (m_length == 0) {
- return 0.0;
+ throw new IndexOutOfBoundsException("removeLast() called on an empty container");
}
m_length--;
@@ -138,8 +149,9 @@
*
* @param size New buffer size.
*/
+ @SuppressWarnings("unchecked")
public void resize(int size) {
- double[] newBuffer = new double[size];
+ var newBuffer = (T[]) new Object[size];
m_length = Math.min(m_length, size);
for (int i = 0; i < m_length; i++) {
newBuffer[i] = m_data[(m_front + i) % m_data.length];
@@ -150,7 +162,6 @@
/** Sets internal buffer contents to zero. */
public void clear() {
- Arrays.fill(m_data, 0.0);
m_front = 0;
m_length = 0;
}
@@ -161,23 +172,25 @@
* @param index Index into the buffer.
* @return Element at index starting from front of buffer.
*/
- public double get(int index) {
+ public T get(int index) {
return m_data[(m_front + index) % m_data.length];
}
/**
- * Increment an index modulo the length of the m_data buffer.
+ * Increment an index modulo the length of the buffer.
*
* @param index Index into the buffer.
+ * @return The incremented index.
*/
private int moduloInc(int index) {
return (index + 1) % m_data.length;
}
/**
- * Decrement an index modulo the length of the m_data buffer.
+ * Decrement an index modulo the length of the buffer.
*
* @param index Index into the buffer.
+ * @return The decremented index.
*/
private int moduloDec(int index) {
if (index == 0) {
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/CombinedRuntimeLoader.java b/wpiutil/src/main/java/edu/wpi/first/util/CombinedRuntimeLoader.java
index 09e739d..8fe4839 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/CombinedRuntimeLoader.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/CombinedRuntimeLoader.java
@@ -16,11 +16,17 @@
import java.util.Map;
import java.util.Objects;
+/** Loads dynamic libraries for all platforms. */
public final class CombinedRuntimeLoader {
private CombinedRuntimeLoader() {}
private static String extractionDirectory;
+ /**
+ * Returns library extraction directory.
+ *
+ * @return Library extraction directory.
+ */
public static synchronized String getExtractionDirectory() {
return extractionDirectory;
}
@@ -29,6 +35,12 @@
extractionDirectory = directory;
}
+ /**
+ * Sets DLL directory.
+ *
+ * @param directory Directory.
+ * @return DLL directory.
+ */
public static native String setDllDirectory(String directory);
private static String getLoadErrorMessage(String libraryName, UnsatisfiedLinkError ule) {
@@ -59,8 +71,7 @@
@SuppressWarnings("unchecked")
public static <T> List<String> extractLibraries(Class<T> clazz, String resourceName)
throws IOException {
- TypeReference<HashMap<String, Object>> typeRef =
- new TypeReference<HashMap<String, Object>>() {};
+ TypeReference<HashMap<String, Object>> typeRef = new TypeReference<>() {};
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map;
try (var stream = clazz.getResourceAsStream(resourceName)) {
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/DoubleCircularBuffer.java b/wpiutil/src/main/java/edu/wpi/first/util/DoubleCircularBuffer.java
new file mode 100644
index 0000000..548f14b
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/DoubleCircularBuffer.java
@@ -0,0 +1,191 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.util;
+
+import java.util.Arrays;
+
+/** This is a simple circular buffer so we don't need to "bucket brigade" copy old values. */
+public class DoubleCircularBuffer {
+ private double[] m_data;
+
+ // Index of element at front of buffer
+ private int m_front;
+
+ // Number of elements used in buffer
+ private int m_length;
+
+ /**
+ * Create a CircularBuffer with the provided size.
+ *
+ * @param size The size of the circular buffer.
+ */
+ public DoubleCircularBuffer(int size) {
+ m_data = new double[size];
+ Arrays.fill(m_data, 0.0);
+ }
+
+ /**
+ * Returns number of elements in buffer.
+ *
+ * @return number of elements in buffer
+ */
+ public int size() {
+ return m_length;
+ }
+
+ /**
+ * Get value at front of buffer.
+ *
+ * @return value at front of buffer
+ */
+ public double getFirst() {
+ return m_data[m_front];
+ }
+
+ /**
+ * Get value at back of buffer.
+ *
+ * @return value at back of buffer
+ */
+ public double getLast() {
+ // If there are no elements in the buffer, do nothing
+ if (m_length == 0) {
+ return 0.0;
+ }
+
+ return m_data[(m_front + m_length - 1) % m_data.length];
+ }
+
+ /**
+ * Push new value onto front of the buffer. The value at the back is overwritten if the buffer is
+ * full.
+ *
+ * @param value The value to push.
+ */
+ public void addFirst(double value) {
+ if (m_data.length == 0) {
+ return;
+ }
+
+ m_front = moduloDec(m_front);
+
+ m_data[m_front] = value;
+
+ if (m_length < m_data.length) {
+ m_length++;
+ }
+ }
+
+ /**
+ * Push new value onto back of the buffer. The value at the front is overwritten if the buffer is
+ * full.
+ *
+ * @param value The value to push.
+ */
+ public void addLast(double value) {
+ if (m_data.length == 0) {
+ return;
+ }
+
+ m_data[(m_front + m_length) % m_data.length] = value;
+
+ if (m_length < m_data.length) {
+ m_length++;
+ } else {
+ // Increment front if buffer is full to maintain size
+ m_front = moduloInc(m_front);
+ }
+ }
+
+ /**
+ * Pop value at front of buffer.
+ *
+ * @return value at front of buffer
+ */
+ public double removeFirst() {
+ // If there are no elements in the buffer, do nothing
+ if (m_length == 0) {
+ return 0.0;
+ }
+
+ double temp = m_data[m_front];
+ m_front = moduloInc(m_front);
+ m_length--;
+ return temp;
+ }
+
+ /**
+ * Pop value at back of buffer.
+ *
+ * @return value at back of buffer
+ */
+ public double removeLast() {
+ // If there are no elements in the buffer, do nothing
+ if (m_length == 0) {
+ return 0.0;
+ }
+
+ m_length--;
+ return m_data[(m_front + m_length) % m_data.length];
+ }
+
+ /**
+ * Resizes internal buffer to given size.
+ *
+ * <p>A new buffer is allocated because arrays are not resizable.
+ *
+ * @param size New buffer size.
+ */
+ public void resize(int size) {
+ double[] newBuffer = new double[size];
+ m_length = Math.min(m_length, size);
+ for (int i = 0; i < m_length; i++) {
+ newBuffer[i] = m_data[(m_front + i) % m_data.length];
+ }
+ m_data = newBuffer;
+ m_front = 0;
+ }
+
+ /** Sets internal buffer contents to zero. */
+ public void clear() {
+ Arrays.fill(m_data, 0.0);
+ m_front = 0;
+ m_length = 0;
+ }
+
+ /**
+ * Get the element at the provided index relative to the start of the buffer.
+ *
+ * @param index Index into the buffer.
+ * @return Element at index starting from front of buffer.
+ */
+ public double get(int index) {
+ return m_data[(m_front + index) % m_data.length];
+ }
+
+ /**
+ * Increment an index modulo the length of the buffer.
+ *
+ * @param index Index into the buffer.
+ * @return The incremented index.
+ */
+ private int moduloInc(int index) {
+ return (index + 1) % m_data.length;
+ }
+
+ /**
+ * Decrement an index modulo the length of the buffer.
+ *
+ * @param index Index into the buffer.
+ * @return The decremented index.
+ */
+ private int moduloDec(int index) {
+ if (index == 0) {
+ return m_data.length - 1;
+ } else {
+ return index - 1;
+ }
+ }
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/EventVector.java b/wpiutil/src/main/java/edu/wpi/first/util/EventVector.java
index 4d2c800..60fb5d1 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/EventVector.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/EventVector.java
@@ -8,10 +8,14 @@
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
+/** A thread-safe container for handling events. */
public class EventVector {
private final ReentrantLock m_lock = new ReentrantLock();
private final List<Integer> m_events = new ArrayList<>();
+ /** Default constructor. */
+ public EventVector() {}
+
/**
* Adds an event to the event vector.
*
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/InterpolatingTreeMap.java b/wpiutil/src/main/java/edu/wpi/first/util/InterpolatingTreeMap.java
index 2c54d00..8efe917 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/InterpolatingTreeMap.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/InterpolatingTreeMap.java
@@ -10,12 +10,17 @@
* Interpolating Tree Maps are used to get values at points that are not defined by making a guess
* from points that are defined. This uses linear interpolation.
*
+ * @param <K> Key type.
+ * @param <V> Value type.
* @deprecated Use {@link edu.wpi.first.math.interpolation.InterpolatingDoubleTreeMap} instead
*/
@Deprecated(forRemoval = true, since = "2024")
public class InterpolatingTreeMap<K extends Number, V extends Number> {
private final TreeMap<K, V> m_map = new TreeMap<>();
+ /** Default constructor. */
+ public InterpolatingTreeMap() {}
+
/**
* Inserts a key-value pair.
*
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/PixelFormat.java b/wpiutil/src/main/java/edu/wpi/first/util/PixelFormat.java
new file mode 100644
index 0000000..809bfd0
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/PixelFormat.java
@@ -0,0 +1,52 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.util;
+
+/** Image pixel format. */
+public enum PixelFormat {
+ /** Unknown format. */
+ kUnknown(0),
+ /** Motion-JPEG (compressed image data). */
+ kMJPEG(1),
+ /** YUY 4:2:2, 16 bpp. */
+ kYUYV(2),
+ /** RGB 5-6-5, 16 bpp. */
+ kRGB565(3),
+ /** BGR 8-8-8, 24 bpp. */
+ kBGR(4),
+ /** Grayscale, 8 bpp. */
+ kGray(5),
+ /** Grayscale, 16 bpp. */
+ kY16(6),
+ /** YUV 4:2:2, 16 bpp. */
+ kUYVY(7);
+
+ private final int value;
+
+ PixelFormat(int value) {
+ this.value = value;
+ }
+
+ /**
+ * Gets the integer value of the pixel format.
+ *
+ * @return Integer value
+ */
+ public int getValue() {
+ return value;
+ }
+
+ private static final PixelFormat[] s_values = values();
+
+ /**
+ * Gets a PixelFormat enum value from its integer value.
+ *
+ * @param pixelFormat integer value
+ * @return Enum value
+ */
+ public static PixelFormat getFromInt(int pixelFormat) {
+ return s_values[pixelFormat];
+ }
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/RawFrame.java b/wpiutil/src/main/java/edu/wpi/first/util/RawFrame.java
new file mode 100644
index 0000000..dd074bf
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/RawFrame.java
@@ -0,0 +1,188 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.util;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Class for storing raw frame data between image read call.
+ *
+ * <p>Data is reused for each frame read, rather then reallocating every frame.
+ */
+public class RawFrame implements AutoCloseable {
+ private long m_nativeObj;
+ private ByteBuffer m_data;
+ private int m_width;
+ private int m_height;
+ private int m_stride;
+ private PixelFormat m_pixelFormat = PixelFormat.kUnknown;
+
+ /** Construct a new empty RawFrame. */
+ public RawFrame() {
+ m_nativeObj = WPIUtilJNI.allocateRawFrame();
+ }
+
+ /**
+ * Close the RawFrame, releasing native resources. Any images currently using the data will be
+ * invalidated.
+ */
+ @Override
+ public void close() {
+ WPIUtilJNI.freeRawFrame(m_nativeObj);
+ m_nativeObj = 0;
+ }
+
+ /**
+ * Called from JNI to set data in class.
+ *
+ * @param data A native ByteBuffer pointing to the frame data.
+ * @param width The width of the frame, in pixels
+ * @param height The height of the frame, in pixels
+ * @param stride The number of bytes in each row of image data
+ * @param pixelFormat The PixelFormat of the frame
+ */
+ void setDataJNI(ByteBuffer data, int width, int height, int stride, int pixelFormat) {
+ m_data = data;
+ m_width = width;
+ m_height = height;
+ m_stride = stride;
+ m_pixelFormat = PixelFormat.getFromInt(pixelFormat);
+ }
+
+ /**
+ * Called from JNI to set info in class.
+ *
+ * @param width The width of the frame, in pixels
+ * @param height The height of the frame, in pixels
+ * @param stride The number of bytes in each row of image data
+ * @param pixelFormat The PixelFormat of the frame
+ */
+ void setInfoJNI(int width, int height, int stride, int pixelFormat) {
+ m_width = width;
+ m_height = height;
+ m_stride = stride;
+ m_pixelFormat = PixelFormat.getFromInt(pixelFormat);
+ }
+
+ /**
+ * Set frame data.
+ *
+ * @param data A native ByteBuffer pointing to the frame data.
+ * @param width The width of the frame, in pixels
+ * @param height The height of the frame, in pixels
+ * @param stride The number of bytes in each row of image data
+ * @param pixelFormat The PixelFormat of the frame
+ */
+ public void setData(ByteBuffer data, int width, int height, int stride, PixelFormat pixelFormat) {
+ if (!data.isDirect()) {
+ throw new UnsupportedOperationException("ByteBuffer must be direct");
+ }
+ m_data = data;
+ m_width = width;
+ m_height = height;
+ m_stride = stride;
+ m_pixelFormat = pixelFormat;
+ WPIUtilJNI.setRawFrameData(
+ m_nativeObj, data, data.limit(), width, height, stride, pixelFormat.getValue());
+ }
+
+ /**
+ * Call to set frame information.
+ *
+ * @param width The width of the frame, in pixels
+ * @param height The height of the frame, in pixels
+ * @param stride The number of bytes in each row of image data
+ * @param pixelFormat The PixelFormat of the frame
+ */
+ public void setInfo(int width, int height, int stride, PixelFormat pixelFormat) {
+ m_width = width;
+ m_height = height;
+ m_stride = stride;
+ m_pixelFormat = pixelFormat;
+ WPIUtilJNI.setRawFrameInfo(
+ m_nativeObj,
+ m_data != null ? m_data.limit() : 0,
+ width,
+ height,
+ stride,
+ pixelFormat.getValue());
+ }
+
+ /**
+ * Get the pointer to native representation of this frame.
+ *
+ * @return The pointer to native representation of this frame.
+ */
+ public long getNativeObj() {
+ return m_nativeObj;
+ }
+
+ /**
+ * Get a ByteBuffer pointing to the frame data. This ByteBuffer is backed by the frame directly.
+ * Its lifetime is controlled by the frame. If a new frame gets read, it will overwrite the
+ * current one.
+ *
+ * @return A ByteBuffer pointing to the frame data.
+ */
+ public ByteBuffer getData() {
+ return m_data;
+ }
+
+ /**
+ * Get a long (is a uint8_t* in native code) pointing to the frame data. This pointer is backed by
+ * the frame directly. Its lifetime is controlled by the frame. If a new frame gets read, it will
+ * overwrite the current one.
+ *
+ * @return A long pointing to the frame data.
+ */
+ public long getDataPtr() {
+ return WPIUtilJNI.getRawFrameDataPtr(m_nativeObj);
+ }
+
+ /**
+ * Get the total size of the data stored in the frame, in bytes.
+ *
+ * @return The total size of the data stored in the frame.
+ */
+ public int getSize() {
+ return m_data != null ? m_data.limit() : 0;
+ }
+
+ /**
+ * Get the width of the image.
+ *
+ * @return The width of the image, in pixels.
+ */
+ public int getWidth() {
+ return m_width;
+ }
+
+ /**
+ * Get the height of the image.
+ *
+ * @return The height of the image, in pixels.
+ */
+ public int getHeight() {
+ return m_height;
+ }
+
+ /**
+ * Get the number of bytes in each row of image data.
+ *
+ * @return The image data stride, in bytes.
+ */
+ public int getStride() {
+ return m_stride;
+ }
+
+ /**
+ * Get the PixelFormat of the frame.
+ *
+ * @return The PixelFormat of the frame.
+ */
+ public PixelFormat getPixelFormat() {
+ return m_pixelFormat;
+ }
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/RuntimeDetector.java b/wpiutil/src/main/java/edu/wpi/first/util/RuntimeDetector.java
index 550339e..72593dc 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/RuntimeDetector.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/RuntimeDetector.java
@@ -6,6 +6,9 @@
import java.io.File;
+/**
+ * A utility class for detecting and providing platform-specific such as OS and CPU architecture.
+ */
public final class RuntimeDetector {
private static String filePrefix;
private static String fileExtension;
@@ -131,32 +134,57 @@
}
/**
- * check if architecture is Arm64.
+ * Check if architecture is Arm64.
*
- * @return if architecture is Arm64
+ * @return if architecture is Arm64.
*/
public static boolean isArm64() {
String arch = System.getProperty("os.arch");
return "aarch64".equals(arch) || "arm64".equals(arch);
}
+ /**
+ * Check if OS is Linux.
+ *
+ * @return if OS is Linux.
+ */
public static boolean isLinux() {
return System.getProperty("os.name").startsWith("Linux");
}
+ /**
+ * Check if OS is Windows.
+ *
+ * @return if OS is Windows.
+ */
public static boolean isWindows() {
return System.getProperty("os.name").startsWith("Windows");
}
+ /**
+ * Check if OS is Mac.
+ *
+ * @return if OS is Mac.
+ */
public static boolean isMac() {
return System.getProperty("os.name").startsWith("Mac");
}
+ /**
+ * Check if OS is 32bit Intel.
+ *
+ * @return if OS is 32bit Intel.
+ */
public static boolean is32BitIntel() {
String arch = System.getProperty("os.arch");
return "x86".equals(arch) || "i386".equals(arch);
}
+ /**
+ * Check if OS is 64bit Intel.
+ *
+ * @return if OS is 64bit Intel.
+ */
public static boolean is64BitIntel() {
String arch = System.getProperty("os.arch");
return "amd64".equals(arch) || "x86_64".equals(arch);
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/RuntimeLoader.java b/wpiutil/src/main/java/edu/wpi/first/util/RuntimeLoader.java
index f24ace3..474666e 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/RuntimeLoader.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/RuntimeLoader.java
@@ -17,6 +17,11 @@
import java.util.Locale;
import java.util.Scanner;
+/**
+ * Loads a native library at runtime.
+ *
+ * @param <T> The class to load.
+ */
public final class RuntimeLoader<T> {
private static String defaultExtractionRoot;
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/WPISerializable.java b/wpiutil/src/main/java/edu/wpi/first/util/WPISerializable.java
new file mode 100644
index 0000000..200deb5
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/WPISerializable.java
@@ -0,0 +1,8 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.util;
+
+/** Marker interface to indicate a class is serializable using WPI serialization methods. */
+public interface WPISerializable {}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/WPIUtilJNI.java b/wpiutil/src/main/java/edu/wpi/first/util/WPIUtilJNI.java
index 9929b48..0bd5b21 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/WPIUtilJNI.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/WPIUtilJNI.java
@@ -5,22 +5,38 @@
package edu.wpi.first.util;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
+/** WPIUtil JNI. */
public class WPIUtilJNI {
static boolean libraryLoaded = false;
static RuntimeLoader<WPIUtilJNI> loader = null;
+ /** Sets whether JNI should be loaded in the static block. */
public static class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
+ /**
+ * Returns true if the JNI should be loaded in the static block.
+ *
+ * @return True if the JNI should be loaded in the static block.
+ */
public static boolean getExtractOnStaticLoad() {
return extractOnStaticLoad.get();
}
+ /**
+ * Sets whether the JNI should be loaded in the static block.
+ *
+ * @param load Whether the JNI should be loaded in the static block.
+ */
public static void setExtractOnStaticLoad(boolean load) {
extractOnStaticLoad.set(load);
}
+
+ /** Utility class. */
+ private Helper() {}
}
static {
@@ -54,32 +70,115 @@
libraryLoaded = true;
}
+ /**
+ * Write the given string to stderr.
+ *
+ * @param str String to write.
+ */
public static native void writeStderr(String str);
+ /** Enable mock time. */
public static native void enableMockTime();
+ /** Disable mock time. */
public static native void disableMockTime();
+ /**
+ * Set mock time.
+ *
+ * @param time The desired time in microseconds.
+ */
public static native void setMockTime(long time);
+ /**
+ * Returns the time.
+ *
+ * @return The time.
+ */
public static native long now();
+ /**
+ * Returns the system time.
+ *
+ * @return The system time.
+ */
public static native long getSystemTime();
+ /**
+ * Creates an event. Events have binary state (signaled or not signaled) and may be either
+ * automatically reset or manually reset. Automatic-reset events go to non-signaled state when a
+ * WaitForObject is woken up by the event; manual-reset events require ResetEvent() to be called
+ * to set the event to non-signaled state; if ResetEvent() is not called, any waiter on that event
+ * will immediately wake when called.
+ *
+ * @param manualReset true for manual reset, false for automatic reset
+ * @param initialState true to make the event initially in signaled state
+ * @return Event handle
+ */
public static native int createEvent(boolean manualReset, boolean initialState);
+ /**
+ * Destroys an event. Destruction wakes up any waiters.
+ *
+ * @param eventHandle event handle
+ */
public static native void destroyEvent(int eventHandle);
+ /**
+ * Sets an event to signaled state.
+ *
+ * @param eventHandle event handle
+ */
public static native void setEvent(int eventHandle);
+ /**
+ * Sets an event to non-signaled state.
+ *
+ * @param eventHandle event handle
+ */
public static native void resetEvent(int eventHandle);
+ /**
+ * Creates a semaphore. Semaphores keep an internal counter. Releasing the semaphore increases the
+ * count. A semaphore with a non-zero count is considered signaled. When a waiter wakes up it
+ * atomically decrements the count by 1. This is generally useful in a single-supplier,
+ * multiple-consumer scenario.
+ *
+ * @param initialCount initial value for the semaphore's internal counter
+ * @param maximumCount maximum value for the samephore's internal counter
+ * @return Semaphore handle
+ */
public static native int createSemaphore(int initialCount, int maximumCount);
+ /**
+ * Destroys a semaphore. Destruction wakes up any waiters.
+ *
+ * @param semHandle semaphore handle
+ */
public static native void destroySemaphore(int semHandle);
+ /**
+ * Releases N counts of a semaphore.
+ *
+ * @param semHandle semaphore handle
+ * @param releaseCount amount to add to semaphore's internal counter; must be positive
+ * @return True on successful release, false on failure (e.g. release count would exceed maximum
+ * value, or handle invalid)
+ */
public static native boolean releaseSemaphore(int semHandle, int releaseCount);
+ static native long allocateRawFrame();
+
+ static native void freeRawFrame(long frame);
+
+ static native long getRawFrameDataPtr(long frame);
+
+ static native void setRawFrameData(
+ long frame, ByteBuffer data, int size, int width, int height, int stride, int pixelFormat);
+
+ static native void setRawFrameInfo(
+ long frame, int size, int width, int height, int stride, int pixelFormat);
+
/**
* Waits for a handle to be signaled.
*
@@ -124,4 +223,7 @@
*/
public static native int[] waitForObjectsTimeout(int[] handles, double timeout)
throws InterruptedException;
+
+ /** Utility class. */
+ protected WPIUtilJNI() {}
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/cleanup/CleanupPool.java b/wpiutil/src/main/java/edu/wpi/first/util/cleanup/CleanupPool.java
index fab8316..4b8c211 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/cleanup/CleanupPool.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/cleanup/CleanupPool.java
@@ -14,7 +14,10 @@
public class CleanupPool implements AutoCloseable {
// Use a Deque instead of a Stack, as Stack's iterators go the wrong way, and docs
// state ArrayDeque is faster anyway.
- private final Deque<AutoCloseable> m_closers = new ArrayDeque<AutoCloseable>();
+ private final Deque<AutoCloseable> m_closers = new ArrayDeque<>();
+
+ /** Default constructor. */
+ public CleanupPool() {}
/**
* Registers an object in the object stack for cleanup.
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/cleanup/SkipCleanup.java b/wpiutil/src/main/java/edu/wpi/first/util/cleanup/SkipCleanup.java
index e2bc72e..fcbcb7e 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/cleanup/SkipCleanup.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/cleanup/SkipCleanup.java
@@ -9,6 +9,7 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+/** Attribute for telling JVM to skip object cleanup. */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SkipCleanup {}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanArrayLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanArrayLogEntry.java
index 718d460..21ac596 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanArrayLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanArrayLogEntry.java
@@ -6,20 +6,49 @@
/** Log array of boolean values. */
public class BooleanArrayLogEntry extends DataLogEntry {
+ /** The data type for boolean array values. */
public static final String kDataType = "boolean[]";
+ /**
+ * Constructs a boolean array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param timestamp entry creation timestamp (0=now)
+ */
public BooleanArrayLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
+ /**
+ * Constructs a boolean array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ */
public BooleanArrayLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
+ /**
+ * Constructs a boolean array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param timestamp entry creation timestamp (0=now)
+ */
public BooleanArrayLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
+ /**
+ * Constructs a boolean array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ */
public BooleanArrayLogEntry(DataLog log, String name) {
this(log, name, 0);
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanLogEntry.java
index 503b83b..c413bfa 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/BooleanLogEntry.java
@@ -6,20 +6,49 @@
/** Log boolean values. */
public class BooleanLogEntry extends DataLogEntry {
+ /** The data type for boolean values. */
public static final String kDataType = "boolean";
+ /**
+ * Constructs a boolean log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param timestamp entry creation timestamp (0=now)
+ */
public BooleanLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
+ /**
+ * Constructs a boolean log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ */
public BooleanLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
+ /**
+ * Constructs a boolean log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param timestamp entry creation timestamp (0=now)
+ */
public BooleanLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
+ /**
+ * Constructs a boolean log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ */
public BooleanLogEntry(DataLog log, String name) {
this(log, name, 0);
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLog.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLog.java
index 97c629f..f48b081 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLog.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLog.java
@@ -494,6 +494,11 @@
DataLogJNI.appendStringArray(m_impl, entry, arr, timestamp);
}
+ /**
+ * Gets the JNI implementation handle.
+ *
+ * @return data log handle.
+ */
public long getImpl() {
return m_impl;
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogEntry.java
index 4beaff2..8502428 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogEntry.java
@@ -6,15 +6,39 @@
/** Log entry base class. */
public class DataLogEntry {
+ /**
+ * Constructs a data log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param type Data type
+ * @param metadata metadata
+ * @param timestamp entry creation timestamp (0=now)
+ */
protected DataLogEntry(DataLog log, String name, String type, String metadata, long timestamp) {
m_log = log;
m_entry = log.start(name, type, metadata, timestamp);
}
+ /**
+ * Constructs a data log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param type Data type
+ * @param metadata metadata
+ */
protected DataLogEntry(DataLog log, String name, String type, String metadata) {
this(log, name, type, metadata, 0);
}
+ /**
+ * Constructs a data log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param type Data type
+ */
protected DataLogEntry(DataLog log, String name, String type) {
this(log, name, type, "");
}
@@ -52,6 +76,9 @@
finish(0);
}
+ /** The data log instance associated with the entry. */
protected final DataLog m_log;
+
+ /** The data log entry index. */
protected final int m_entry;
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogJNI.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogJNI.java
index f94a86f..c764339 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogJNI.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogJNI.java
@@ -7,35 +7,145 @@
import edu.wpi.first.util.WPIUtilJNI;
import java.nio.ByteBuffer;
+/**
+ * DataLog wpiutil JNI Functions.
+ *
+ * @see "wpiutil/DataLog.h"
+ */
public class DataLogJNI extends WPIUtilJNI {
+ /**
+ * Create a new Data Log. The log will be initially created with a temporary filename.
+ *
+ * @param dir directory to store the log
+ * @param filename filename to use; if none provided, a random filename is generated of the form
+ * "wpilog_{}.wpilog"
+ * @param period time between automatic flushes to disk, in seconds; this is a time/storage
+ * tradeoff
+ * @param extraHeader extra header data
+ * @return data log implementation handle
+ */
static native long create(String dir, String filename, double period, String extraHeader);
+ /**
+ * Change log filename.
+ *
+ * @param impl data log implementation handle
+ * @param filename filename
+ */
static native void setFilename(long impl, String filename);
+ /**
+ * Explicitly flushes the log data to disk.
+ *
+ * @param impl data log implementation handle
+ */
static native void flush(long impl);
+ /**
+ * Pauses appending of data records to the log. While paused, no data records are saved (e.g.
+ * AppendX is a no-op). Has no effect on entry starts / finishes / metadata changes.
+ *
+ * @param impl data log implementation handle
+ */
static native void pause(long impl);
+ /**
+ * Resumes appending of data records to the log. If called after Stop(), opens a new file (with
+ * random name if SetFilename was not called after Stop()) and appends Start records and schema
+ * data values for all previously started entries and schemas.
+ *
+ * @param impl data log implementation handle
+ */
static native void resume(long impl);
+ /**
+ * Stops appending all records to the log, and closes the log file.
+ *
+ * @param impl data log implementation handle
+ */
static native void stop(long impl);
+ /**
+ * Registers a data schema. Data schemas provide information for how a certain data type string
+ * can be decoded. The type string of a data schema indicates the type of the schema itself (e.g.
+ * "protobuf" for protobuf schemas, "struct" for struct schemas, etc). In the data log, schemas
+ * are saved just like normal records, with the name being generated from the provided name:
+ * "/.schema/<name>". Duplicate calls to this function with the same name are silently
+ * ignored.
+ *
+ * @param impl data log implementation handle
+ * @param name Name (the string passed as the data type for records using this schema)
+ * @param type Type of schema (e.g. "protobuf", "struct", etc)
+ * @param schema Schema data
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void addSchema(long impl, String name, String type, byte[] schema, long timestamp);
static native void addSchemaString(
long impl, String name, String type, String schema, long timestamp);
+ /**
+ * Start an entry. Duplicate names are allowed (with the same type), and result in the same index
+ * being returned (Start/Finish are reference counted). A duplicate name with a different type
+ * will result in an error message being printed to the console and 0 being returned (which will
+ * be ignored by the Append functions).
+ *
+ * @param impl data log implementation handle
+ * @param name Name
+ * @param type Data type
+ * @param metadata Initial metadata (e.g. data properties)
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ * @return Entry index
+ */
static native int start(long impl, String name, String type, String metadata, long timestamp);
+ /**
+ * Finish an entry.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void finish(long impl, int entry, long timestamp);
+ /**
+ * Updates the metadata for an entry.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index
+ * @param metadata New metadata for the entry
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void setMetadata(long impl, int entry, String metadata, long timestamp);
+ /**
+ * Closes the data log implementation handle.
+ *
+ * @param impl data log implementation handle
+ */
static native void close(long impl);
+ /**
+ * Appends a raw record to the log.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param data Byte array to record
+ * @param len Length of byte array
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void appendRaw(
long impl, int entry, byte[] data, int start, int len, long timestamp);
+ /**
+ * Appends a raw record to the log.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index, as returned by WPI_DataLog_Start()
+ * @param data ByteBuffer to record
+ * @param len Length of byte array
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static void appendRaw(long impl, int entry, ByteBuffer data, int start, int len, long timestamp) {
if (data.isDirect()) {
if (start < 0) {
@@ -58,23 +168,106 @@
private static native void appendRawBuffer(
long impl, int entry, ByteBuffer data, int start, int len, long timestamp);
+ /**
+ * Appends a boolean record to the log.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index, as returned by Start()
+ * @param value Boolean value to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void appendBoolean(long impl, int entry, boolean value, long timestamp);
+ /**
+ * Appends an integer record to the log.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index, as returned by Start()
+ * @param value Integer value to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void appendInteger(long impl, int entry, long value, long timestamp);
+ /**
+ * Appends a float record to the log.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index, as returned by Start()
+ * @param value Float value to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void appendFloat(long impl, int entry, float value, long timestamp);
+ /**
+ * Appends a double record to the log.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index, as returned by Start()
+ * @param value Double value to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void appendDouble(long impl, int entry, double value, long timestamp);
+ /**
+ * Appends a string record to the log.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index, as returned by Start()
+ * @param value String value to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void appendString(long impl, int entry, String value, long timestamp);
+ /**
+ * Appends a boolean array record to the log.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index, as returned by Start()
+ * @param arr Boolean array to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void appendBooleanArray(long impl, int entry, boolean[] value, long timestamp);
+ /**
+ * Appends an integer array record to the log.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index, as returned by Start()
+ * @param arr Integer array to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void appendIntegerArray(long impl, int entry, long[] value, long timestamp);
+ /**
+ * Appends a float array record to the log.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index, as returned by Start()
+ * @param arr Float array to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void appendFloatArray(long impl, int entry, float[] value, long timestamp);
+ /**
+ * Appends a double array record to the log.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index, as returned by Start()
+ * @param arr Double array to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void appendDoubleArray(long impl, int entry, double[] value, long timestamp);
+ /**
+ * Appends a string array record to the log.
+ *
+ * @param impl data log implementation handle
+ * @param entry Entry index, as returned by Start()
+ * @param arr String array to record
+ * @param timestamp Time stamp (may be 0 to indicate now)
+ */
static native void appendStringArray(long impl, int entry, String[] value, long timestamp);
+
+ /** Utility class. */
+ private DataLogJNI() {}
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleArrayLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleArrayLogEntry.java
index 67ef8c3..485a9c8 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleArrayLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleArrayLogEntry.java
@@ -6,20 +6,49 @@
/** Log array of double values. */
public class DoubleArrayLogEntry extends DataLogEntry {
+ /** The data type for double array values. */
public static final String kDataType = "double[]";
+ /**
+ * Constructs a double array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param timestamp entry creation timestamp (0=now)
+ */
public DoubleArrayLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
+ /**
+ * Constructs a double array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ */
public DoubleArrayLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
+ /**
+ * Constructs a double array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param timestamp entry creation timestamp (0=now)
+ */
public DoubleArrayLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
+ /**
+ * Constructs a double array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ */
public DoubleArrayLogEntry(DataLog log, String name) {
this(log, name, 0);
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleLogEntry.java
index f16c27e..a089df2 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/DoubleLogEntry.java
@@ -6,20 +6,49 @@
/** Log double values. */
public class DoubleLogEntry extends DataLogEntry {
+ /** The data type for double values. */
public static final String kDataType = "double";
+ /**
+ * Constructs a double log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param timestamp entry creation timestamp (0=now)
+ */
public DoubleLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
+ /**
+ * Constructs a double log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ */
public DoubleLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
+ /**
+ * Constructs a double log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param timestamp entry creation timestamp (0=now)
+ */
public DoubleLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
+ /**
+ * Constructs a double log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ */
public DoubleLogEntry(DataLog log, String name) {
this(log, name, 0);
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatArrayLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatArrayLogEntry.java
index 3a0b7e0..be25970 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatArrayLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatArrayLogEntry.java
@@ -6,20 +6,49 @@
/** Log array of float values. */
public class FloatArrayLogEntry extends DataLogEntry {
+ /** The data type for float array values. */
public static final String kDataType = "float[]";
+ /**
+ * Constructs a float array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param timestamp entry creation timestamp (0=now)
+ */
public FloatArrayLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
+ /**
+ * Constructs a float array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ */
public FloatArrayLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
+ /**
+ * Constructs a float array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param timestamp entry creation timestamp (0=now)
+ */
public FloatArrayLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
+ /**
+ * Constructs a float array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ */
public FloatArrayLogEntry(DataLog log, String name) {
this(log, name, 0);
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatLogEntry.java
index 28adc34..28f83cb 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/FloatLogEntry.java
@@ -6,20 +6,49 @@
/** Log float values. */
public class FloatLogEntry extends DataLogEntry {
+ /** The data type for float values. */
public static final String kDataType = "float";
+ /**
+ * Constructs a float log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param timestamp entry creation timestamp (0=now)
+ */
public FloatLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
+ /**
+ * Constructs a float log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ */
public FloatLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
+ /**
+ * Constructs a float log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param timestamp entry creation timestamp (0=now)
+ */
public FloatLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
+ /**
+ * Constructs a float log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ */
public FloatLogEntry(DataLog log, String name) {
this(log, name, 0);
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerArrayLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerArrayLogEntry.java
index 2cffc8d..d2f8f0e 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerArrayLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerArrayLogEntry.java
@@ -6,20 +6,49 @@
/** Log array of integer values. */
public class IntegerArrayLogEntry extends DataLogEntry {
+ /** The data type for integer array values. */
public static final String kDataType = "int64[]";
+ /**
+ * Constructs a integer array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param timestamp entry creation timestamp (0=now)
+ */
public IntegerArrayLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
+ /**
+ * Constructs a integer array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ */
public IntegerArrayLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
+ /**
+ * Constructs a integer array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param timestamp entry creation timestamp (0=now)
+ */
public IntegerArrayLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
+ /**
+ * Constructs a integer array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ */
public IntegerArrayLogEntry(DataLog log, String name) {
this(log, name, 0);
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerLogEntry.java
index 142ca5d..395a208 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/IntegerLogEntry.java
@@ -6,20 +6,49 @@
/** Log integer values. */
public class IntegerLogEntry extends DataLogEntry {
+ /** The data type for integer values. */
public static final String kDataType = "int64";
+ /**
+ * Constructs a integer log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param timestamp entry creation timestamp (0=now)
+ */
public IntegerLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
+ /**
+ * Constructs a integer log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ */
public IntegerLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
+ /**
+ * Constructs a integer log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param timestamp entry creation timestamp (0=now)
+ */
public IntegerLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
+ /**
+ * Constructs a integer log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ */
public IntegerLogEntry(DataLog log, String name) {
this(log, name, 0);
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/ProtobufLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/ProtobufLogEntry.java
index 1db2647..9e7fa43 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/ProtobufLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/ProtobufLogEntry.java
@@ -37,7 +37,7 @@
*/
public static <T, MessageType extends ProtoMessage<?>> ProtobufLogEntry<T> create(
DataLog log, String name, Protobuf<T, MessageType> proto, String metadata, long timestamp) {
- return new ProtobufLogEntry<T>(log, name, proto, metadata, timestamp);
+ return new ProtobufLogEntry<>(log, name, proto, metadata, timestamp);
}
/**
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/RawLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/RawLogEntry.java
index 972fc03..a9e3373 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/RawLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/RawLogEntry.java
@@ -8,28 +8,74 @@
/** Log raw byte array values. */
public class RawLogEntry extends DataLogEntry {
+ /** The data type for raw values. */
public static final String kDataType = "raw";
+ /**
+ * Constructs a raw log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param type Data type
+ * @param timestamp entry creation timestamp (0=now)
+ */
public RawLogEntry(DataLog log, String name, String metadata, String type, long timestamp) {
super(log, name, type, metadata, timestamp);
}
+ /**
+ * Constructs a raw log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param type Data type
+ */
public RawLogEntry(DataLog log, String name, String metadata, String type) {
this(log, name, metadata, type, 0);
}
+ /**
+ * Constructs a raw log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param timestamp entry creation timestamp (0=now)
+ */
public RawLogEntry(DataLog log, String name, String metadata, long timestamp) {
this(log, name, metadata, kDataType, timestamp);
}
+ /**
+ * Constructs a raw log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ */
public RawLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
+ /**
+ * Constructs a raw log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param timestamp entry creation timestamp (0=now)
+ */
public RawLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
+ /**
+ * Constructs a raw log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ */
public RawLogEntry(DataLog log, String name) {
this(log, name, 0);
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringArrayLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringArrayLogEntry.java
index 37bdeb1..f0a6dde 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringArrayLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringArrayLogEntry.java
@@ -6,20 +6,49 @@
/** Log array of string values. */
public class StringArrayLogEntry extends DataLogEntry {
+ /** The data type for string array values. */
public static final String kDataType = "string[]";
+ /**
+ * Constructs a string array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param timestamp entry creation timestamp (0=now)
+ */
public StringArrayLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
+ /**
+ * Constructs a string array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ */
public StringArrayLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
+ /**
+ * Constructs a string array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param timestamp entry creation timestamp (0=now)
+ */
public StringArrayLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
+ /**
+ * Constructs a string array log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ */
public StringArrayLogEntry(DataLog log, String name) {
this(log, name, 0);
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringLogEntry.java
index 0722dc0..27c8aef 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StringLogEntry.java
@@ -6,28 +6,74 @@
/** Log string values. */
public class StringLogEntry extends DataLogEntry {
+ /** The data type for string values. */
public static final String kDataType = "string";
+ /**
+ * Constructs a String log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param type Data type
+ * @param timestamp entry creation timestamp (0=now)
+ */
public StringLogEntry(DataLog log, String name, String metadata, String type, long timestamp) {
super(log, name, type, metadata, timestamp);
}
+ /**
+ * Constructs a String log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param type Data type
+ */
public StringLogEntry(DataLog log, String name, String metadata, String type) {
this(log, name, metadata, type, 0);
}
+ /**
+ * Constructs a String log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ * @param timestamp entry creation timestamp (0=now)
+ */
public StringLogEntry(DataLog log, String name, String metadata, long timestamp) {
this(log, name, metadata, kDataType, timestamp);
}
+ /**
+ * Constructs a String log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param metadata metadata
+ */
public StringLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
+ /**
+ * Constructs a String log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ * @param timestamp entry creation timestamp (0=now)
+ */
public StringLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
+ /**
+ * Constructs a String log entry.
+ *
+ * @param log datalog
+ * @param name name of the entry
+ */
public StringLogEntry(DataLog log, String name) {
this(log, name, 0);
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructArrayLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructArrayLogEntry.java
index 0f6cb2e..b3a31c9 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructArrayLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructArrayLogEntry.java
@@ -35,7 +35,7 @@
*/
public static <T> StructArrayLogEntry<T> create(
DataLog log, String name, Struct<T> struct, String metadata, long timestamp) {
- return new StructArrayLogEntry<T>(log, name, struct, metadata, timestamp);
+ return new StructArrayLogEntry<>(log, name, struct, metadata, timestamp);
}
/**
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructLogEntry.java b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructLogEntry.java
index a227c32..0d09182 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructLogEntry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/datalog/StructLogEntry.java
@@ -34,7 +34,7 @@
*/
public static <T> StructLogEntry<T> create(
DataLog log, String name, Struct<T> struct, String metadata, long timestamp) {
- return new StructLogEntry<T>(log, name, struct, metadata, timestamp);
+ return new StructLogEntry<>(log, name, struct, metadata, timestamp);
}
/**
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufBuffer.java b/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufBuffer.java
index af3e466..1f8fdf0 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufBuffer.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufBuffer.java
@@ -26,9 +26,17 @@
m_proto = proto;
}
+ /**
+ * Creates a ProtobufBuffer for the given Protobuf object.
+ *
+ * @param <T> The type to serialize.
+ * @param <MessageType> The Protobuf message type.
+ * @param proto The Protobuf object.
+ * @return A ProtobufBuffer for the given Protobuf object.
+ */
public static <T, MessageType extends ProtoMessage<?>> ProtobufBuffer<T, MessageType> create(
Protobuf<T, MessageType> proto) {
- return new ProtobufBuffer<T, MessageType>(proto);
+ return new ProtobufBuffer<>(proto);
}
/**
@@ -61,7 +69,7 @@
m_msg.clearQuick();
m_proto.pack(m_msg, value);
int size = m_msg.getSerializedSize();
- if (size < m_buf.capacity()) {
+ if (size > m_buf.capacity()) {
m_buf = ByteBuffer.allocateDirect(size * 2);
m_sink.setOutput(m_buf);
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufSerializable.java b/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufSerializable.java
new file mode 100644
index 0000000..ac75065
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/protobuf/ProtobufSerializable.java
@@ -0,0 +1,15 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.util.protobuf;
+
+import edu.wpi.first.util.WPISerializable;
+
+/**
+ * Marker interface to indicate a class is serializable using Protobuf serialization.
+ *
+ * <p>While this cannot be enforced by the interface, any class implementing this interface should
+ * provide a public final static `proto` member variable.
+ */
+public interface ProtobufSerializable extends WPISerializable {}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java b/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java
index db822ce..ee93725 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java
@@ -15,10 +15,14 @@
import java.util.function.LongSupplier;
import java.util.function.Supplier;
+/** Helper class for building Sendable dashboard representations. */
public interface SendableBuilder extends AutoCloseable {
/** The backend kinds used for the sendable builder. */
enum BackendKind {
+ /** Unknown. */
kUnknown,
+
+ /** NetworkTables. */
kNetworkTables
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableRegistry.java b/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableRegistry.java
index 6f0ad41..025b802 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableRegistry.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableRegistry.java
@@ -506,6 +506,9 @@
/** Sendable builder for the sendable. */
public SendableBuilder builder;
+
+ /** Default constructor. */
+ public CallbackData() {}
}
// As foreachLiveWindow is single threaded, cache the components it
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/BadSchemaException.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/BadSchemaException.java
index 6ff2236..9c69e1f 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/struct/BadSchemaException.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/BadSchemaException.java
@@ -4,34 +4,70 @@
package edu.wpi.first.util.struct;
+/** Exception thrown when encountering a bad schema. */
public class BadSchemaException extends Exception {
+ /** The bad schema field. */
private final String m_field;
- public BadSchemaException(String s) {
- super(s);
+ /**
+ * Constructs a BadSchemaException.
+ *
+ * @param message the detail message.
+ */
+ public BadSchemaException(String message) {
+ super(message);
m_field = "";
}
+ /**
+ * Constructs a BadSchemaException.
+ *
+ * @param message the detail message.
+ * @param cause The cause (which is saved for later retrieval by the {@link #getCause()} method).
+ */
public BadSchemaException(String message, Throwable cause) {
super(message, cause);
m_field = "";
}
+ /**
+ * Constructs a BadSchemaException.
+ *
+ * @param cause The cause (which is saved for later retrieval by the {@link #getCause()} method).
+ */
public BadSchemaException(Throwable cause) {
super(cause);
m_field = "";
}
- public BadSchemaException(String field, String s) {
- super(s);
+ /**
+ * Constructs a BadSchemaException.
+ *
+ * @param field The bad schema field.
+ * @param message the detail message.
+ */
+ public BadSchemaException(String field, String message) {
+ super(message);
m_field = field;
}
+ /**
+ * Constructs a BadSchemaException.
+ *
+ * @param field The bad schema field.
+ * @param message the detail message.
+ * @param cause The cause (which is saved for later retrieval by the {@link #getCause()} method).
+ */
public BadSchemaException(String field, String message, Throwable cause) {
super(message, cause);
m_field = field;
}
+ /**
+ * Gets the name of the bad schema field.
+ *
+ * @return The name of the bad schema field, or an empty string if not applicable.
+ */
public String getField() {
return m_field;
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/DynamicStruct.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/DynamicStruct.java
index 165f7db..1f9fdc3 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/struct/DynamicStruct.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/DynamicStruct.java
@@ -593,24 +593,24 @@
case 1:
{
byte val = m_data.get(field.m_offset + arrIndex);
- val &= ~(field.getBitMask() << field.m_bitShift);
- val |= (value & field.getBitMask()) << field.m_bitShift;
+ val &= (byte) ~(field.getBitMask() << field.m_bitShift);
+ val |= (byte) ((value & field.getBitMask()) << field.m_bitShift);
m_data.put(field.m_offset + arrIndex, val);
break;
}
case 2:
{
short val = m_data.getShort(field.m_offset + arrIndex * 2);
- val &= ~(field.getBitMask() << field.m_bitShift);
- val |= (value & field.getBitMask()) << field.m_bitShift;
+ val &= (short) ~(field.getBitMask() << field.m_bitShift);
+ val |= (short) ((value & field.getBitMask()) << field.m_bitShift);
m_data.putShort(field.m_offset + arrIndex * 2, val);
break;
}
case 4:
{
int val = m_data.getInt(field.m_offset + arrIndex * 4);
- val &= ~(field.getBitMask() << field.m_bitShift);
- val |= (value & field.getBitMask()) << field.m_bitShift;
+ val &= (int) ~(field.getBitMask() << field.m_bitShift);
+ val |= (int) ((value & field.getBitMask()) << field.m_bitShift);
m_data.putInt(field.m_offset + arrIndex * 4, val);
break;
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructBuffer.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructBuffer.java
index 0e8aa18..a5d04e0 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructBuffer.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructBuffer.java
@@ -12,7 +12,7 @@
/**
* Reusable buffer for serialization/deserialization to/from a raw struct.
*
- * @param <T> object type
+ * @param <T> Object type.
*/
public final class StructBuffer<T> {
private StructBuffer(Struct<T> struct) {
@@ -21,8 +21,15 @@
m_struct = struct;
}
+ /**
+ * Returns a StructBuffer for the given struct.
+ *
+ * @param struct A struct.
+ * @param <T> Object type.
+ * @return A StructBuffer for the given struct.
+ */
public static <T> StructBuffer<T> create(Struct<T> struct) {
- return new StructBuffer<T>(struct);
+ return new StructBuffer<>(struct);
}
/**
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptorDatabase.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptorDatabase.java
index be7343f..459fa9e 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptorDatabase.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructDescriptorDatabase.java
@@ -14,6 +14,9 @@
/** Database of raw struct dynamic descriptors. */
public class StructDescriptorDatabase {
+ /** Default constructor. */
+ public StructDescriptorDatabase() {}
+
/**
* Adds a structure schema to the database. If the struct references other structs that have not
* yet been added, it will not be valid until those structs are also added.
@@ -33,7 +36,7 @@
}
// turn parsed schema into descriptors
- StructDescriptor theStruct = m_structs.computeIfAbsent(name, k -> new StructDescriptor(k));
+ StructDescriptor theStruct = m_structs.computeIfAbsent(name, StructDescriptor::new);
theStruct.m_schema = schema;
theStruct.m_fields.clear();
boolean isValid = true;
@@ -76,7 +79,7 @@
// cross-reference struct, creating a placeholder if necessary
StructDescriptor aStruct =
- m_structs.computeIfAbsent(decl.typeString, k -> new StructDescriptor(k));
+ m_structs.computeIfAbsent(decl.typeString, StructDescriptor::new);
// if the struct isn't valid, we can't be valid either
if (aStruct.isValid()) {
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldType.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldType.java
index 28d5d8e..4b3cf17 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldType.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFieldType.java
@@ -6,29 +6,46 @@
/** Known data types for raw struct dynamic fields (see StructFieldDescriptor). */
public enum StructFieldType {
+ /** bool. */
kBool("bool", false, false, 1),
+ /** char. */
kChar("char", false, false, 1),
+ /** int8. */
kInt8("int8", true, false, 1),
+ /** int16. */
kInt16("int16", true, false, 2),
+ /** int32. */
kInt32("int32", true, false, 4),
+ /** int64. */
kInt64("int64", true, false, 8),
+ /** uint8. */
kUint8("uint8", false, true, 1),
+ /** uint16. */
kUint16("uint16", false, true, 2),
+ /** uint32. */
kUint32("uint32", false, true, 4),
+ /** uint64. */
kUint64("uint64", false, true, 8),
+ /** float. */
kFloat("float", false, false, 4),
+ /** double. */
kDouble("double", false, false, 8),
+ /** struct. */
kStruct("struct", false, false, 0);
+ /** The name of the data type. */
@SuppressWarnings("MemberName")
public final String name;
+ /** Indicates if the data type is a signed integer. */
@SuppressWarnings("MemberName")
public final boolean isInt;
+ /** Indicates if the data type is an unsigned integer. */
@SuppressWarnings("MemberName")
public final boolean isUint;
+ /** The size (in bytes) of the data type. */
@SuppressWarnings("MemberName")
public final int size;
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructSerializable.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructSerializable.java
new file mode 100644
index 0000000..a8d1fdd
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructSerializable.java
@@ -0,0 +1,15 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.util.struct;
+
+import edu.wpi.first.util.WPISerializable;
+
+/**
+ * Marker interface to indicate a class is serializable using Struct serialization.
+ *
+ * <p>While this cannot be enforced by the interface, any class implementing this interface should
+ * provide a public final static `struct` member variable.
+ */
+public interface StructSerializable extends WPISerializable {}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParseException.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParseException.java
index 9fa843c..5e71c79 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParseException.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParseException.java
@@ -4,24 +4,50 @@
package edu.wpi.first.util.struct.parser;
+/** Exception for parsing errors. */
public class ParseException extends Exception {
+ /** The parser position. */
private final int m_pos;
+ /**
+ * Constructs a ParseException.
+ *
+ * @param pos The parser position.
+ * @param s Reason for parse failure.
+ */
public ParseException(int pos, String s) {
super(s);
m_pos = pos;
}
+ /**
+ * Constructs a ParseException.
+ *
+ * @param pos The parser position.
+ * @param message Reason for parse failure.
+ * @param cause Exception that caused the parser failure.
+ */
public ParseException(int pos, String message, Throwable cause) {
super(message, cause);
m_pos = pos;
}
+ /**
+ * Constructs a ParseException.
+ *
+ * @param pos The parser position.
+ * @param cause Exception that caused the parser failure.
+ */
public ParseException(int pos, Throwable cause) {
super(cause);
m_pos = pos;
}
+ /**
+ * Returns position in parsed string.
+ *
+ * @return Position in parsed string.
+ */
public int getPosition() {
return m_pos;
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedDeclaration.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedDeclaration.java
index 8184ae5..29743ab 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedDeclaration.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedDeclaration.java
@@ -8,18 +8,26 @@
/** Raw struct schema declaration. */
public class ParsedDeclaration {
+ /** Type string. */
@SuppressWarnings("MemberName")
public String typeString;
+ /** Name. */
@SuppressWarnings("MemberName")
public String name;
+ /** Enum values. */
@SuppressWarnings("MemberName")
public Map<String, Long> enumValues;
+ /** Array size. */
@SuppressWarnings("MemberName")
public int arraySize = 1;
+ /** Bit width. */
@SuppressWarnings("MemberName")
public int bitWidth;
+
+ /** Default constructor. */
+ public ParsedDeclaration() {}
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedSchema.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedSchema.java
index 2ca1753..2f7312a 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedSchema.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/ParsedSchema.java
@@ -9,6 +9,10 @@
/** Raw struct schema. */
public class ParsedSchema {
+ /** Declarations. */
@SuppressWarnings("MemberName")
public List<ParsedDeclaration> declarations = new ArrayList<>();
+
+ /** Default constructor. */
+ public ParsedSchema() {}
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/TokenKind.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/TokenKind.java
index 85afa4a..fb74c6f 100644
--- a/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/TokenKind.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/parser/TokenKind.java
@@ -6,17 +6,40 @@
/** A lexed raw struct schema token. */
public enum TokenKind {
+ /** Unknown. */
kUnknown("unknown"),
+
+ /** Integer. */
kInteger("integer"),
+
+ /** Identifier. */
kIdentifier("identifier"),
+
+ /** Left square bracket. */
kLeftBracket("'['"),
+
+ /** Right square bracket. */
kRightBracket("']'"),
+
+ /** Left curly brace. */
kLeftBrace("'{'"),
+
+ /** Right curly brace. */
kRightBrace("'}'"),
+
+ /** Colon. */
kColon("':'"),
+
+ /** Semicolon. */
kSemicolon("';'"),
+
+ /** Comma. */
kComma("','"),
+
+ /** Equals. */
kEquals("'='"),
+
+ /** End of input. */
kEndOfInput("<EOF>");
private final String m_name;
diff --git a/wpiutil/src/main/native/cpp/DataLog.cpp b/wpiutil/src/main/native/cpp/DataLog.cpp
index d05a49e..40dab2a 100644
--- a/wpiutil/src/main/native/cpp/DataLog.cpp
+++ b/wpiutil/src/main/native/cpp/DataLog.cpp
@@ -179,7 +179,7 @@
DataLog::~DataLog() {
{
std::scoped_lock lock{m_mutex};
- m_state = kShutdown;
+ m_shutdown = true;
m_doFlush = true;
}
m_cond.notify_all();
@@ -419,7 +419,7 @@
uintmax_t written = 0;
std::unique_lock lock{m_mutex};
- while (m_state != kShutdown) {
+ do {
bool doFlush = false;
auto timeoutTime = std::chrono::steady_clock::now() + periodTime;
if (m_cond.wait_until(lock, timeoutTime) == std::cv_status::timeout) {
@@ -557,7 +557,7 @@
}
toWrite.resize(0);
}
- }
+ } while (!m_shutdown);
}
void DataLog::WriterThreadMain(
@@ -580,7 +580,7 @@
std::vector<Buffer> toWrite;
std::unique_lock lock{m_mutex};
- while (m_state != kShutdown) {
+ do {
bool doFlush = false;
auto timeoutTime = std::chrono::steady_clock::now() + periodTime;
if (m_cond.wait_until(lock, timeoutTime) == std::cv_status::timeout) {
@@ -614,7 +614,7 @@
}
toWrite.resize(0);
}
- }
+ } while (!m_shutdown);
write({}); // indicate EOF
}
@@ -743,8 +743,10 @@
std::memcpy(buf, data.data(), kBlockSize);
data = data.subspan(kBlockSize);
}
- uint8_t* buf = Reserve(data.size());
- std::memcpy(buf, data.data(), data.size());
+ if (!data.empty()) {
+ uint8_t* buf = Reserve(data.size());
+ std::memcpy(buf, data.data(), data.size());
+ }
}
void DataLog::AppendStringImpl(std::string_view str) {
diff --git a/wpiutil/src/main/native/cpp/RawFrame.cpp b/wpiutil/src/main/native/cpp/RawFrame.cpp
new file mode 100644
index 0000000..2bc36ed
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/RawFrame.cpp
@@ -0,0 +1,49 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpi/RawFrame.h"
+
+#include <wpi/MemAlloc.h>
+
+#include <cstring>
+
+extern "C" {
+int WPI_AllocateRawFrameData(WPI_RawFrame* frame, size_t requestedSize) {
+ if (frame->capacity >= requestedSize) {
+ return 0;
+ }
+ WPI_FreeRawFrameData(frame);
+ frame->data = static_cast<uint8_t*>(wpi::safe_malloc(requestedSize));
+ frame->capacity = requestedSize;
+ frame->size = 0;
+ return 1;
+}
+
+void WPI_FreeRawFrameData(WPI_RawFrame* frame) {
+ if (frame->data) {
+ if (frame->freeFunc) {
+ frame->freeFunc(frame->freeCbData, frame->data, frame->capacity);
+ } else {
+ std::free(frame->data);
+ }
+ frame->data = nullptr;
+ frame->freeFunc = nullptr;
+ frame->freeCbData = nullptr;
+ frame->capacity = 0;
+ }
+}
+
+void WPI_SetRawFrameData(WPI_RawFrame* frame, void* data, size_t size,
+ size_t capacity, void* cbdata,
+ void (*freeFunc)(void* cbdata, void* data,
+ size_t capacity)) {
+ WPI_FreeRawFrameData(frame);
+ frame->data = static_cast<uint8_t*>(data);
+ frame->freeFunc = freeFunc;
+ frame->freeCbData = cbdata;
+ frame->capacity = capacity;
+ frame->size = size;
+}
+
+} // extern "C"
diff --git a/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
index eb55fd0..26d1c93 100644
--- a/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
+++ b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
@@ -9,6 +9,7 @@
#include <fmt/format.h>
#include "edu_wpi_first_util_WPIUtilJNI.h"
+#include "wpi/RawFrame.h"
#include "wpi/Synchronization.h"
#include "wpi/jni_util.h"
#include "wpi/timestamp.h"
@@ -317,4 +318,95 @@
return MakeJIntArray(env, signaled);
}
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: allocateRawFrame
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_allocateRawFrame
+ (JNIEnv*, jclass)
+{
+ return reinterpret_cast<jlong>(new wpi::RawFrame);
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: freeRawFrame
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_freeRawFrame
+ (JNIEnv*, jclass, jlong frame)
+{
+ delete reinterpret_cast<wpi::RawFrame*>(frame);
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: getRawFrameDataPtr
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_getRawFrameDataPtr
+ (JNIEnv* env, jclass, jlong frame)
+{
+ auto* f = reinterpret_cast<wpi::RawFrame*>(frame);
+ if (!f) {
+ wpi::ThrowNullPointerException(env, "frame is null");
+ return 0;
+ }
+ return reinterpret_cast<jlong>(f->data);
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: setRawFrameData
+ * Signature: (JLjava/lang/Object;IIIII)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_setRawFrameData
+ (JNIEnv* env, jclass, jlong frame, jobject data, jint size, jint width,
+ jint height, jint stride, jint pixelFormat)
+{
+ auto* f = reinterpret_cast<wpi::RawFrame*>(frame);
+ if (!f) {
+ wpi::ThrowNullPointerException(env, "frame is null");
+ return;
+ }
+ auto buf = env->GetDirectBufferAddress(data);
+ if (!buf) {
+ wpi::ThrowNullPointerException(env, "data is null");
+ return;
+ }
+ // there's no way to free a passed-in direct byte buffer
+ f->SetData(buf, size, env->GetDirectBufferCapacity(data), nullptr,
+ [](void*, void*, size_t) {});
+ f->width = width;
+ f->height = height;
+ f->stride = stride;
+ f->pixelFormat = pixelFormat;
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: setRawFrameInfo
+ * Signature: (JIIIII)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_setRawFrameInfo
+ (JNIEnv* env, jclass, jlong frame, jint size, jint width, jint height,
+ jint stride, jint pixelFormat)
+{
+ auto* f = reinterpret_cast<wpi::RawFrame*>(frame);
+ if (!f) {
+ wpi::ThrowNullPointerException(env, "frame is null");
+ return;
+ }
+ f->width = width;
+ f->height = height;
+ f->stride = stride;
+ f->pixelFormat = pixelFormat;
+}
+
} // extern "C"
diff --git a/wpiutil/src/main/native/cpp/timestamp.cpp b/wpiutil/src/main/native/cpp/timestamp.cpp
index c7e2fa9..c811964 100644
--- a/wpiutil/src/main/native/cpp/timestamp.cpp
+++ b/wpiutil/src/main/native/cpp/timestamp.cpp
@@ -5,6 +5,7 @@
#include "wpi/timestamp.h"
#include <atomic>
+#include <optional>
#ifdef __FRC_ROBORIO__
#include <stdint.h>
@@ -12,9 +13,7 @@
#pragma GCC diagnostic ignored "-Wpedantic"
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#include <FRC_FPGA_ChipObject/RoboRIO_FRC_ChipObject_Aliases.h>
-#include <FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/nInterfaceGlobals.h>
#include <FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tHMB.h>
-#include <FRC_NetworkCommunication/LoadOut.h>
#pragma GCC diagnostic pop
namespace fpga {
using namespace nFPGA;
@@ -50,64 +49,107 @@
const char* memoryName,
size_t* memorySize,
void** virtualAddress);
-struct HMBHolder {
- ~HMBHolder() {
- if (hmb) {
- closeHmb(hmb->getSystemInterface()->getHandle(), hmbName);
- dlclose(niFpga);
- }
- }
- explicit operator bool() const { return hmb != nullptr; }
- void Configure() {
- nFPGA::nRoboRIO_FPGANamespace::g_currentTargetClass =
- nLoadOut::getTargetClass();
+using NiFpga_FindRegisterFunc = NiFpga_Status (*)(NiFpga_Session session,
+ const char* registerName,
+ uint32_t* registerOffset);
+using NiFpga_ReadU32Func = NiFpga_Status (*)(NiFpga_Session session,
+ uint32_t indicator,
+ uint32_t* value);
+using NiFpga_WriteU32Func = NiFpga_Status (*)(NiFpga_Session session,
+ uint32_t control, uint32_t value);
+static void dlcloseWrapper(void* handle) {
+ dlclose(handle);
+}
+static std::atomic_flag hmbInitialized = ATOMIC_FLAG_INIT;
+static std::atomic_flag nowUseDefaultOnFailure = ATOMIC_FLAG_INIT;
+struct HMBLowLevel {
+ ~HMBLowLevel() { Reset(); }
+ bool Configure(const NiFpga_Session session) {
int32_t status = 0;
- hmb.reset(fpga::tHMB::create(&status));
- niFpga = dlopen("libNiFpga.so", RTLD_LAZY);
+ niFpga.reset(dlopen("libNiFpga.so", RTLD_LAZY));
if (!niFpga) {
- hmb = nullptr;
- return;
+ fmt::print(stderr, "Could not open libNiFpga.so\n");
+ return false;
}
NiFpga_OpenHmbFunc openHmb = reinterpret_cast<NiFpga_OpenHmbFunc>(
- dlsym(niFpga, "NiFpgaDll_OpenHmb"));
+ dlsym(niFpga.get(), "NiFpgaDll_OpenHmb"));
closeHmb = reinterpret_cast<NiFpga_CloseHmbFunc>(
- dlsym(niFpga, "NiFpgaDll_CloseHmb"));
- if (openHmb == nullptr || closeHmb == nullptr) {
+ dlsym(niFpga.get(), "NiFpgaDll_CloseHmb"));
+ NiFpga_FindRegisterFunc findRegister =
+ reinterpret_cast<NiFpga_FindRegisterFunc>(
+ dlsym(niFpga.get(), "NiFpgaDll_FindRegister"));
+ NiFpga_ReadU32Func readU32 = reinterpret_cast<NiFpga_ReadU32Func>(
+ dlsym(niFpga.get(), "NiFpgaDll_ReadU32"));
+ NiFpga_WriteU32Func writeU32 = reinterpret_cast<NiFpga_WriteU32Func>(
+ dlsym(niFpga.get(), "NiFpgaDll_WriteU32"));
+ if (openHmb == nullptr || closeHmb == nullptr || findRegister == nullptr ||
+ writeU32 == nullptr || readU32 == nullptr) {
+ fmt::print(stderr, "Could not find HMB symbols in libNiFpga.so\n");
+ niFpga = nullptr;
+ return false;
+ }
+ uint32_t hmbConfigRegister = 0;
+ status = findRegister(session, "HMB.Config", &hmbConfigRegister);
+ if (status != 0) {
+ fmt::print(stderr, "Failed to find HMB.Config register, status code {}\n",
+ status);
closeHmb = nullptr;
- dlclose(niFpga);
- hmb = nullptr;
- return;
+ niFpga = nullptr;
+ return false;
}
size_t hmbBufferSize = 0;
status =
- openHmb(hmb->getSystemInterface()->getHandle(), hmbName, &hmbBufferSize,
+ openHmb(session, hmbName, &hmbBufferSize,
reinterpret_cast<void**>(const_cast<uint32_t**>(&hmbBuffer)));
if (status != 0) {
+ fmt::print(stderr, "Failed to open HMB, status code {}\n", status);
closeHmb = nullptr;
- dlclose(niFpga);
- hmb = nullptr;
- return;
+ niFpga = nullptr;
+ return false;
}
- auto cfg = hmb->readConfig(&status);
+ fpga::tHMB::tConfig cfg;
+ uint32_t read = 0;
+ status = readU32(session, hmbConfigRegister, &read);
+ cfg.value = read;
cfg.Enables_Timestamp = 1;
- hmb->writeConfig(cfg, &status);
+ status = writeU32(session, hmbConfigRegister, cfg.value);
+ hmbSession.emplace(session);
+ hmbInitialized.test_and_set();
+ return true;
}
void Reset() {
- if (hmb) {
- std::unique_ptr<fpga::tHMB> oldHmb;
- oldHmb.swap(hmb);
- closeHmb(oldHmb->getSystemInterface()->getHandle(), hmbName);
- closeHmb = nullptr;
- hmbBuffer = nullptr;
- oldHmb.reset();
- dlclose(niFpga);
+ hmbInitialized.clear();
+ std::optional<NiFpga_Session> oldSesh;
+ hmbSession.swap(oldSesh);
+ if (oldSesh.has_value()) {
+ closeHmb(oldSesh.value(), hmbName);
niFpga = nullptr;
}
}
- std::unique_ptr<fpga::tHMB> hmb;
- void* niFpga = nullptr;
+ std::optional<NiFpga_Session> hmbSession;
NiFpga_CloseHmbFunc closeHmb = nullptr;
volatile uint32_t* hmbBuffer = nullptr;
+ std::unique_ptr<void, decltype(&dlcloseWrapper)> niFpga{nullptr,
+ dlcloseWrapper};
+};
+struct HMBHolder {
+ void Configure(void* col, std::unique_ptr<fpga::tHMB> hmbObject) {
+ hmb = std::move(hmbObject);
+ chipObjectLibrary.reset(col);
+ if (!lowLevel.Configure(hmb->getSystemInterface()->getHandle())) {
+ hmb = nullptr;
+ chipObjectLibrary = nullptr;
+ }
+ }
+ void Reset() {
+ lowLevel.Reset();
+ hmb = nullptr;
+ chipObjectLibrary = nullptr;
+ }
+ HMBLowLevel lowLevel;
+ std::unique_ptr<fpga::tHMB> hmb;
+ std::unique_ptr<void, decltype(&dlcloseWrapper)> chipObjectLibrary{
+ nullptr, dlcloseWrapper};
};
static HMBHolder hmb;
} // namespace
@@ -186,10 +228,26 @@
static std::atomic<uint64_t (*)()> now_impl{wpi::NowDefault};
-void wpi::impl::SetupNowRio() {
+void wpi::impl::SetupNowDefaultOnRio() {
#ifdef __FRC_ROBORIO__
- if (!hmb) {
- hmb.Configure();
+ nowUseDefaultOnFailure.test_and_set();
+#endif
+}
+
+#ifdef __FRC_ROBORIO__
+template <>
+void wpi::impl::SetupNowRio(void* chipObjectLibrary,
+ std::unique_ptr<fpga::tHMB> hmbObject) {
+ if (!hmbInitialized.test()) {
+ hmb.Configure(chipObjectLibrary, std::move(hmbObject));
+ }
+}
+#endif
+
+void wpi::impl::SetupNowRio(uint32_t session) {
+#ifdef __FRC_ROBORIO__
+ if (!hmbInitialized.test()) {
+ hmb.lowLevel.Configure(session);
}
#endif
}
@@ -207,25 +265,29 @@
uint64_t wpi::Now() {
#ifdef __FRC_ROBORIO__
// Same code as HAL_GetFPGATime()
- if (!hmb) {
- std::fputs(
- "FPGA not yet configured in wpi::Now(). Time will not be correct",
- stderr);
- std::fflush(stderr);
- return 0;
+ if (!hmbInitialized.test()) {
+ if (nowUseDefaultOnFailure.test()) {
+ return timestamp() - offset_val;
+ } else {
+ fmt::print(
+ stderr,
+ "FPGA not yet configured in wpi::Now(). Time will not be correct.\n");
+ std::fflush(stderr);
+ return 1;
+ }
}
asm("dmb");
- uint64_t upper1 = hmb.hmbBuffer[timestampUpperOffset];
+ uint64_t upper1 = hmb.lowLevel.hmbBuffer[timestampUpperOffset];
asm("dmb");
- uint32_t lower = hmb.hmbBuffer[timestampLowerOffset];
+ uint32_t lower = hmb.lowLevel.hmbBuffer[timestampLowerOffset];
asm("dmb");
- uint64_t upper2 = hmb.hmbBuffer[timestampUpperOffset];
+ uint64_t upper2 = hmb.lowLevel.hmbBuffer[timestampUpperOffset];
if (upper1 != upper2) {
// Rolled over between the lower call, reread lower
asm("dmb");
- lower = hmb.hmbBuffer[timestampLowerOffset];
+ lower = hmb.lowLevel.hmbBuffer[timestampLowerOffset];
}
// 5 is added here because the time to write from the FPGA
// to the HMB buffer is longer then the time to read
@@ -244,8 +306,12 @@
extern "C" {
-void WPI_Impl_SetupNowRio(void) {
- return wpi::impl::SetupNowRio();
+void WPI_Impl_SetupNowUseDefaultOnRio(void) {
+ return wpi::impl::SetupNowDefaultOnRio();
+}
+
+void WPI_Impl_SetupNowRioWithSession(uint32_t session) {
+ return wpi::impl::SetupNowRio(session);
}
void WPI_Impl_ShutdownNowRio(void) {
diff --git a/wpiutil/src/main/native/include/wpi/Algorithm.h b/wpiutil/src/main/native/include/wpi/Algorithm.h
index 1fd2502..112bfc3 100644
--- a/wpiutil/src/main/native/include/wpi/Algorithm.h
+++ b/wpiutil/src/main/native/include/wpi/Algorithm.h
@@ -5,6 +5,8 @@
#pragma once
#include <algorithm>
+#include <cstddef>
+#include <utility>
#include <vector>
namespace wpi {
@@ -15,4 +17,19 @@
T const& item) {
return vec.insert(std::upper_bound(vec.begin(), vec.end(), item), item);
}
+
+/**
+ * Calls f(i, elem) for each element of elems where i is the index of the
+ * element in elems and elem is the element.
+ *
+ * @param f The callback.
+ * @param elems The elements.
+ */
+template <typename F, typename... Ts>
+constexpr void for_each(F&& f, Ts&&... elems) {
+ [&]<size_t... Is>(std::index_sequence<Is...>) {
+ (f(Is, elems), ...);
+ }(std::index_sequence_for<Ts...>{});
+}
+
} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/DataLog.h b/wpiutil/src/main/native/include/wpi/DataLog.h
index 99db964..2f79a3a 100644
--- a/wpiutil/src/main/native/include/wpi/DataLog.h
+++ b/wpiutil/src/main/native/include/wpi/DataLog.h
@@ -16,6 +16,7 @@
#include <string>
#include <string_view>
#include <thread>
+#include <tuple>
#include <utility>
#include <vector>
#include <version>
@@ -263,16 +264,20 @@
* name are silently ignored.
*
* @tparam T struct serializable type
+ * @param info optional struct type info
* @param timestamp Time stamp (0 to indicate now)
*/
- template <StructSerializable T>
- void AddStructSchema(int64_t timestamp = 0) {
+ template <typename T, typename... I>
+ requires StructSerializable<T, I...>
+ void AddStructSchema(const I&... info, int64_t timestamp = 0) {
if (timestamp == 0) {
timestamp = Now();
}
- ForEachStructSchema<T>([this, timestamp](auto typeString, auto schema) {
- AddSchema(typeString, "structschema", schema, timestamp);
- });
+ ForEachStructSchema<T>(
+ [this, timestamp](auto typeString, auto schema) {
+ this->AddSchema(typeString, "structschema", schema, timestamp);
+ },
+ info...);
}
/**
@@ -486,12 +491,12 @@
mutable wpi::mutex m_mutex;
wpi::condition_variable m_cond;
bool m_doFlush{false};
+ bool m_shutdown{false};
enum State {
kStart,
kActive,
kPaused,
kStopped,
- kShutdown,
} m_state = kActive;
double m_period;
std::string m_extraHeader;
@@ -946,19 +951,22 @@
/**
* Log raw struct serializable objects.
*/
-template <StructSerializable T>
+template <typename T, typename... I>
+ requires StructSerializable<T, I...>
class StructLogEntry : public DataLogEntry {
- using S = Struct<T>;
+ using S = Struct<T, I...>;
public:
StructLogEntry() = default;
- StructLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
- : StructLogEntry{log, name, {}, timestamp} {}
+ StructLogEntry(DataLog& log, std::string_view name, I... info,
+ int64_t timestamp = 0)
+ : StructLogEntry{log, name, {}, std::move(info)..., timestamp} {}
StructLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
- int64_t timestamp = 0) {
+ I... info, int64_t timestamp = 0)
+ : m_info{std::move(info)...} {
m_log = &log;
- log.AddStructSchema<T>(timestamp);
- m_entry = log.Start(name, S::kTypeString, metadata, timestamp);
+ log.AddStructSchema<T, I...>(info..., timestamp);
+ m_entry = log.Start(name, S::GetTypeString(info...), metadata, timestamp);
}
/**
@@ -968,31 +976,46 @@
* @param timestamp Time stamp (may be 0 to indicate now)
*/
void Append(const T& data, int64_t timestamp = 0) {
- uint8_t buf[S::kSize];
- S::Pack(buf, data);
+ if constexpr (sizeof...(I) == 0) {
+ if constexpr (wpi::is_constexpr([] { S::GetSize(); })) {
+ uint8_t buf[S::GetSize()];
+ S::Pack(buf, data);
+ m_log->AppendRaw(m_entry, buf, timestamp);
+ return;
+ }
+ }
+ wpi::SmallVector<uint8_t, 128> buf;
+ buf.resize_for_overwrite(std::apply(S::GetSize, m_info));
+ std::apply([&](const I&... info) { S::Pack(buf, data, info...); }, m_info);
m_log->AppendRaw(m_entry, buf, timestamp);
}
+
+ private:
+ [[no_unique_address]] std::tuple<I...> m_info;
};
/**
* Log raw struct serializable array of objects.
*/
-template <StructSerializable T>
+template <typename T, typename... I>
+ requires StructSerializable<T, I...>
class StructArrayLogEntry : public DataLogEntry {
- using S = Struct<T>;
+ using S = Struct<T, I...>;
public:
StructArrayLogEntry() = default;
- StructArrayLogEntry(DataLog& log, std::string_view name,
+ StructArrayLogEntry(DataLog& log, std::string_view name, I... info,
int64_t timestamp = 0)
- : StructArrayLogEntry{log, name, {}, timestamp} {}
+ : StructArrayLogEntry{log, name, {}, std::move(info)..., timestamp} {}
StructArrayLogEntry(DataLog& log, std::string_view name,
- std::string_view metadata, int64_t timestamp = 0) {
+ std::string_view metadata, I... info,
+ int64_t timestamp = 0)
+ : m_info{std::move(info)...} {
m_log = &log;
- log.AddStructSchema<T>(timestamp);
- m_entry =
- log.Start(name, MakeStructArrayTypeString<T, std::dynamic_extent>(),
- metadata, timestamp);
+ log.AddStructSchema<T, I...>(info..., timestamp);
+ m_entry = log.Start(
+ name, MakeStructArrayTypeString<T, std::dynamic_extent>(info...),
+ metadata, timestamp);
}
/**
@@ -1007,9 +1030,14 @@
std::convertible_to<std::ranges::range_value_t<U>, T>
#endif
void Append(U&& data, int64_t timestamp = 0) {
- m_buf.Write(std::forward<U>(data), [&](auto bytes) {
- m_log->AppendRaw(m_entry, bytes, timestamp);
- });
+ std::apply(
+ [&](const I&... info) {
+ m_buf.Write(
+ std::forward<U>(data),
+ [&](auto bytes) { m_log->AppendRaw(m_entry, bytes, timestamp); },
+ info...);
+ },
+ m_info);
}
/**
@@ -1019,12 +1047,19 @@
* @param timestamp Time stamp (may be 0 to indicate now)
*/
void Append(std::span<const T> data, int64_t timestamp = 0) {
- m_buf.Write(
- data, [&](auto bytes) { m_log->AppendRaw(m_entry, bytes, timestamp); });
+ std::apply(
+ [&](const I&... info) {
+ m_buf.Write(
+ data,
+ [&](auto bytes) { m_log->AppendRaw(m_entry, bytes, timestamp); },
+ info...);
+ },
+ m_info);
}
private:
- StructArrayBuffer<T> m_buf;
+ StructArrayBuffer<T, I...> m_buf;
+ [[no_unique_address]] std::tuple<I...> m_info;
};
/**
diff --git a/wpiutil/src/main/native/include/wpi/DataLogReader.h b/wpiutil/src/main/native/include/wpi/DataLogReader.h
index b1153e4..cb9a8cb 100644
--- a/wpiutil/src/main/native/include/wpi/DataLogReader.h
+++ b/wpiutil/src/main/native/include/wpi/DataLogReader.h
@@ -288,7 +288,7 @@
pointer operator->() const { return &this->operator*(); }
- private:
+ protected:
const DataLogReader* m_reader;
size_t m_pos;
mutable bool m_valid = false;
diff --git a/wpiutil/src/main/native/include/wpi/Demangle.h b/wpiutil/src/main/native/include/wpi/Demangle.h
index 03a7d3f..8514be3 100644
--- a/wpiutil/src/main/native/include/wpi/Demangle.h
+++ b/wpiutil/src/main/native/include/wpi/Demangle.h
@@ -7,6 +7,7 @@
#include <string>
#include <string_view>
+#include <typeinfo>
namespace wpi {
@@ -18,6 +19,15 @@
*/
std::string Demangle(std::string_view mangledSymbol);
+/**
+ * Returns the type name of an object
+ * @param type The object
+ */
+template <typename T>
+std::string GetTypeName(const T& type) {
+ return Demangle(typeid(type).name());
+}
+
} // namespace wpi
#endif // WPIUTIL_WPI_DEMANGLE_H_
diff --git a/wpiutil/src/main/native/include/wpi/RawFrame.h b/wpiutil/src/main/native/include/wpi/RawFrame.h
new file mode 100644
index 0000000..1345ff1
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/RawFrame.h
@@ -0,0 +1,142 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPIUTIL_WPI_RAWFRAME_H_
+#define WPIUTIL_WPI_RAWFRAME_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+#include <concepts>
+#include <cstddef>
+#else
+
+#include <stddef.h> // NOLINT
+
+#endif
+
+#ifdef WPI_RAWFRAME_JNI
+#include "jni_util.h"
+#endif
+
+// NOLINT
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Raw Frame
+ */
+typedef struct WPI_RawFrame { // NOLINT
+ // image data
+ uint8_t* data;
+ // function to free image data (may be NULL)
+ void (*freeFunc)(void* cbdata, void* data, size_t capacity);
+ void* freeCbData; // data passed to freeFunc
+ size_t capacity; // data buffer capacity, in bytes
+ size_t size; // actual size of data, in bytes
+ int pixelFormat; // WPI_PixelFormat
+ int width; // width of image, in pixels
+ int height; // height of image, in pixels
+ int stride; // size of each row of data, in bytes (may be 0)
+} WPI_RawFrame;
+
+/**
+ * Pixel formats
+ */
+enum WPI_PixelFormat {
+ WPI_PIXFMT_UNKNOWN = 0, // unknown
+ WPI_PIXFMT_MJPEG, // Motion-JPEG (compressed image data)
+ WPI_PIXFMT_YUYV, // YUV 4:2:2, 16 bpp
+ WPI_PIXFMT_RGB565, // RGB 5-6-5, 16 bpp
+ WPI_PIXFMT_BGR, // BGR 8-8-8, 24 bpp
+ WPI_PIXFMT_GRAY, // Grayscale, 8 bpp
+ WPI_PIXFMT_Y16, // Grayscale, 16 bpp
+ WPI_PIXFMT_UYVY, // YUV 4:2:2, 16 bpp
+};
+
+// Returns nonzero if the frame data was allocated/reallocated
+int WPI_AllocateRawFrameData(WPI_RawFrame* frame, size_t requestedSize);
+void WPI_FreeRawFrameData(WPI_RawFrame* frame);
+void WPI_SetRawFrameData(WPI_RawFrame* frame, void* data, size_t size,
+ size_t capacity, void* cbdata,
+ void (*freeFunc)(void* cbdata, void* data,
+ size_t capacity));
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#ifdef __cplusplus
+namespace wpi {
+struct RawFrame : public WPI_RawFrame {
+ RawFrame() {
+ data = nullptr;
+ freeFunc = nullptr;
+ freeCbData = nullptr;
+ capacity = 0;
+ size = 0;
+ pixelFormat = WPI_PIXFMT_UNKNOWN;
+ width = 0;
+ height = 0;
+ }
+ RawFrame(const RawFrame&) = delete;
+ RawFrame& operator=(const RawFrame&) = delete;
+ RawFrame(RawFrame&& rhs) noexcept : WPI_RawFrame{rhs} {
+ rhs.data = nullptr;
+ rhs.freeFunc = nullptr;
+ rhs.freeCbData = nullptr;
+ rhs.capacity = 0;
+ rhs.size = 0;
+ }
+ RawFrame& operator=(RawFrame&& rhs) noexcept {
+ *static_cast<WPI_RawFrame*>(this) = rhs;
+ rhs.data = nullptr;
+ rhs.freeFunc = nullptr;
+ rhs.freeCbData = nullptr;
+ rhs.capacity = 0;
+ rhs.size = 0;
+ return *this;
+ }
+
+ void SetData(void* data, size_t size, size_t capacity, void* cbdata,
+ void (*freeFunc)(void* cbdata, void* data, size_t capacity)) {
+ WPI_SetRawFrameData(this, data, size, capacity, cbdata, freeFunc);
+ }
+
+ // returns true if the frame data was allocated/reallocated
+ bool Reserve(size_t size) {
+ return WPI_AllocateRawFrameData(this, size) != 0;
+ }
+
+ ~RawFrame() { WPI_FreeRawFrameData(this); }
+};
+
+#ifdef WPI_RAWFRAME_JNI
+template <std::same_as<wpi::RawFrame> T>
+void SetFrameData(JNIEnv* env, jclass rawFrameCls, jobject jframe,
+ const T& frame, bool newData) {
+ if (newData) {
+ static jmethodID setData = env->GetMethodID(rawFrameCls, "setDataJNI",
+ "(Ljava/nio/ByteBuffer;IIII)V");
+ env->CallVoidMethod(
+ jframe, setData, env->NewDirectByteBuffer(frame.data, frame.size),
+ static_cast<jint>(frame.width), static_cast<jint>(frame.height),
+ static_cast<jint>(frame.stride), static_cast<jint>(frame.pixelFormat));
+ } else {
+ static jmethodID setInfo =
+ env->GetMethodID(rawFrameCls, "setInfoJNI", "(IIII)V");
+ env->CallVoidMethod(jframe, setInfo, static_cast<jint>(frame.width),
+ static_cast<jint>(frame.height),
+ static_cast<jint>(frame.stride),
+ static_cast<jint>(frame.pixelFormat));
+ }
+}
+#endif
+
+} // namespace wpi
+#endif
+
+#endif // WPIUTIL_WPI_RAWFRAME_H_
diff --git a/wpiutil/src/main/native/include/wpi/circular_buffer.h b/wpiutil/src/main/native/include/wpi/circular_buffer.h
index c54e2f5..a40a627 100644
--- a/wpiutil/src/main/native/include/wpi/circular_buffer.h
+++ b/wpiutil/src/main/native/include/wpi/circular_buffer.h
@@ -5,6 +5,7 @@
#pragma once
#include <cstddef>
+#include <iterator>
#include <vector>
namespace wpi {
@@ -12,10 +13,17 @@
/**
* This is a simple circular buffer so we don't need to "bucket brigade" copy
* old values.
+ *
+ * @tparam T Buffer element type.
*/
template <class T>
class circular_buffer {
public:
+ /**
+ * Constructs a circular buffer.
+ *
+ * @param size Maximum number of buffer elements.
+ */
explicit circular_buffer(size_t size) : m_data(size, T{}) {}
circular_buffer(const circular_buffer&) = default;
@@ -32,7 +40,7 @@
using reference = T&;
iterator(circular_buffer* buffer, size_t index)
- : m_buffer(buffer), m_index(index) {}
+ : m_buffer{buffer}, m_index{index} {}
iterator& operator++() {
++m_index;
@@ -60,7 +68,7 @@
using const_reference = const T&;
const_iterator(const circular_buffer* buffer, size_t index)
- : m_buffer(buffer), m_index(index) {}
+ : m_buffer{buffer}, m_index{index} {}
const_iterator& operator++() {
++m_index;
@@ -268,16 +276,18 @@
size_t m_length = 0;
/**
- * Increment an index modulo the length of the buffer.
+ * Increment an index modulo the size of the buffer.
*
- * @return The result of the modulo operation.
+ * @param index Index into the buffer.
+ * @return The incremented index.
*/
size_t ModuloInc(size_t index) { return (index + 1) % m_data.size(); }
/**
- * Decrement an index modulo the length of the buffer.
+ * Decrement an index modulo the size of the buffer.
*
- * @return The result of the modulo operation.
+ * @param index Index into the buffer.
+ * @return The decremented index.
*/
size_t ModuloDec(size_t index) {
if (index == 0) {
diff --git a/wpiutil/src/main/native/include/wpi/ct_string.h b/wpiutil/src/main/native/include/wpi/ct_string.h
index 9f0ef90..2c7796b 100644
--- a/wpiutil/src/main/native/include/wpi/ct_string.h
+++ b/wpiutil/src/main/native/include/wpi/ct_string.h
@@ -31,7 +31,7 @@
template <size_t M>
requires(M <= (N + 1))
- consteval ct_string(Char const (&s)[M]) { // NOLINT
+ constexpr ct_string(Char const (&s)[M]) { // NOLINT
if constexpr (M == (N + 1)) {
if (s[N] != Char{}) {
throw std::logic_error{"char array not null terminated"};
@@ -50,7 +50,7 @@
}
}
- explicit consteval ct_string(std::basic_string_view<Char, Traits> s) {
+ explicit constexpr ct_string(std::basic_string_view<Char, Traits> s) {
// avoid dependency on <algorithm>
// auto p = std::ranges::copy(s, chars.begin()).out;
auto p = chars.begin();
@@ -63,6 +63,64 @@
}
}
+ constexpr bool operator==(const ct_string<Char, Traits, N>&) const = default;
+
+ constexpr bool operator==(const std::basic_string<Char, Traits>& rhs) const {
+ if (size() != rhs.size()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < size(); ++i) {
+ if (chars[i] != rhs[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ constexpr bool operator==(std::basic_string_view<Char, Traits> rhs) const {
+ if (size() != rhs.size()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < size(); ++i) {
+ if (chars[i] != rhs[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ template <size_t M>
+ requires(N + 1 == M)
+ constexpr bool operator==(Char const (&rhs)[M]) const {
+ for (size_t i = 0; i < M; ++i) {
+ if (chars[i] != rhs[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ constexpr bool operator==(const Char* rhs) const {
+ for (size_t i = 0; i < N + 1; ++i) {
+ if (chars[i] != rhs[i]) {
+ return false;
+ }
+
+ // If index of rhs's null terminator is less than lhs's size - 1, rhs is
+ // shorter than lhs
+ if (rhs[i] == '\0' && i < N) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
constexpr auto size() const noexcept { return N; }
constexpr auto begin() const noexcept { return chars.begin(); }
@@ -71,6 +129,11 @@
constexpr auto data() const noexcept { return chars.data(); }
constexpr auto c_str() const noexcept { return chars.data(); }
+ constexpr operator std::basic_string<Char, Traits>() // NOLINT
+ const noexcept {
+ return std::basic_string<Char, Traits>{chars.data(), N};
+ }
+
constexpr operator std::basic_string_view<Char, Traits>() // NOLINT
const noexcept {
return std::basic_string_view<Char, Traits>{chars.data(), N};
@@ -82,13 +145,13 @@
inline namespace literals {
template <ct_string S>
-consteval auto operator""_ct_string() {
+constexpr auto operator""_ct_string() {
return S;
}
} // namespace literals
template <typename Char, typename Traits, size_t N1, size_t N2>
-consteval auto operator+(ct_string<Char, Traits, N1> const& s1,
+constexpr auto operator+(ct_string<Char, Traits, N1> const& s1,
ct_string<Char, Traits, N2> const& s2) noexcept {
return Concat(s1, s2);
}
@@ -102,7 +165,7 @@
* @return concatenated string
*/
template <typename Char, typename Traits, size_t N1, size_t... N>
-consteval auto Concat(ct_string<Char, Traits, N1> const& s1,
+constexpr auto Concat(ct_string<Char, Traits, N1> const& s1,
ct_string<Char, Traits, N> const&... s) {
// Need a dummy array to instantiate a ct_string.
constexpr Char dummy[1] = {};
@@ -136,7 +199,7 @@
template <intmax_t N, int Base = 10, typename Char = char,
typename Traits = std::char_traits<Char>>
requires(Base >= 2 && Base <= 36)
-consteval auto NumToCtString() {
+constexpr auto NumToCtString() {
constexpr char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
auto buflen = [] {
diff --git a/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h b/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h
index 4115420..1913d7c 100644
--- a/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h
+++ b/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h
@@ -15,12 +15,20 @@
namespace wpi {
+/**
+ * Helper class for building Sendable dashboard representations.
+ */
class SendableBuilder {
public:
/**
* The backend kinds used for the sendable builder.
*/
- enum BackendKind { kUnknown, kNetworkTables };
+ enum BackendKind {
+ /// Unknown.
+ kUnknown,
+ /// NetworkTables.
+ kNetworkTables
+ };
virtual ~SendableBuilder() = default;
diff --git a/wpiutil/src/main/native/include/wpi/static_circular_buffer.h b/wpiutil/src/main/native/include/wpi/static_circular_buffer.h
index 76498a1..0d38f25 100644
--- a/wpiutil/src/main/native/include/wpi/static_circular_buffer.h
+++ b/wpiutil/src/main/native/include/wpi/static_circular_buffer.h
@@ -6,12 +6,16 @@
#include <array>
#include <cstddef>
+#include <iterator>
namespace wpi {
/**
* This is a simple circular buffer so we don't need to "bucket brigade" copy
* old values.
+ *
+ * @tparam T Buffer element type.
+ * @tparam N Maximum number of buffer elements.
*/
template <class T, size_t N>
class static_circular_buffer {
@@ -27,7 +31,7 @@
using reference = T&;
iterator(static_circular_buffer* buffer, size_t index)
- : m_buffer(buffer), m_index(index) {}
+ : m_buffer{buffer}, m_index{index} {}
iterator& operator++() {
++m_index;
@@ -55,7 +59,7 @@
using const_reference = const T&;
const_iterator(const static_circular_buffer* buffer, size_t index)
- : m_buffer(buffer), m_index(index) {}
+ : m_buffer{buffer}, m_index{index} {}
const_iterator& operator++() {
++m_index;
diff --git a/wpiutil/src/main/native/include/wpi/struct/DynamicStruct.h b/wpiutil/src/main/native/include/wpi/struct/DynamicStruct.h
index b88894c..4254090 100644
--- a/wpiutil/src/main/native/include/wpi/struct/DynamicStruct.h
+++ b/wpiutil/src/main/native/include/wpi/struct/DynamicStruct.h
@@ -32,18 +32,31 @@
* Known data types for raw struct dynamic fields (see StructFieldDescriptor).
*/
enum class StructFieldType {
+ /// bool.
kBool,
+ /// char.
kChar,
+ /// int8.
kInt8,
+ /// int16.
kInt16,
+ /// int32.
kInt32,
+ /// int64.
kInt64,
+ /// uint8.
kUint8,
+ /// uint16.
kUint16,
+ /// uint32.
kUint32,
+ /// uint64.
kUint64,
+ /// float.
kFloat,
+ /// double.
kDouble,
+ /// struct.
kStruct
};
diff --git a/wpiutil/src/main/native/include/wpi/struct/SchemaParser.h b/wpiutil/src/main/native/include/wpi/struct/SchemaParser.h
index a8cf1a4..4e5a1df 100644
--- a/wpiutil/src/main/native/include/wpi/struct/SchemaParser.h
+++ b/wpiutil/src/main/native/include/wpi/struct/SchemaParser.h
@@ -17,18 +17,31 @@
* A lexed raw struct schema token.
*/
struct Token {
+ /** A lexed raw struct schema token kind. */
enum Kind {
+ /// Unknown.
kUnknown,
+ /// Integer.
kInteger,
+ /// Identifier.
kIdentifier,
+ /// Left square bracket.
kLeftBracket,
+ /// Right square bracket.
kRightBracket,
+ /// Left curly brace.
kLeftBrace,
+ /// Right curly brace.
kRightBrace,
+ /// Colon.
kColon,
+ /// Semicolon.
kSemicolon,
+ /// Comma.
kComma,
+ /// Equals.
kEquals,
+ /// End of input.
kEndOfInput,
};
diff --git a/wpiutil/src/main/native/include/wpi/struct/Struct.h b/wpiutil/src/main/native/include/wpi/struct/Struct.h
index 1336707..085b50b 100644
--- a/wpiutil/src/main/native/include/wpi/struct/Struct.h
+++ b/wpiutil/src/main/native/include/wpi/struct/Struct.h
@@ -7,6 +7,7 @@
#include <stdint.h>
#include <concepts>
+#include <memory>
#include <span>
#include <string>
#include <string_view>
@@ -14,12 +15,14 @@
#include <utility>
#include <vector>
+#include <fmt/format.h>
+
#include "wpi/Endian.h"
-#include "wpi/MathExtras.h"
#include "wpi/bit.h"
#include "wpi/ct_string.h"
#include "wpi/function_ref.h"
#include "wpi/mutex.h"
+#include "wpi/type_traits.h"
namespace wpi {
@@ -29,8 +32,9 @@
* StructSerializable concept.
*
* @tparam T type to serialize/deserialize
+ * @tparam I optional struct type info
*/
-template <typename T>
+template <typename T, typename... I>
struct Struct {};
/**
@@ -45,37 +49,47 @@
* Implementations must define a template specialization for wpi::Struct with
* T being the type that is being serialized/deserialized, with the following
* static members (as enforced by this concept):
- * - std::string_view kTypeString: the type string
- * - size_t kSize: the structure size in bytes
- * - std::string_view kSchema: the struct schema
- * - T Unpack(std::span<const uint8_t, kSize>): function for deserialization
- * - void Pack(std::span<uint8_t, kSize>, T&& value): function for
+ * - std::string_view GetTypeString(): function that returns the type string
+ * - size_t GetSize(): function that returns the structure size in bytes
+ * - std::string_view GetSchema(): function that returns the struct schema
+ * - T Unpack(std::span<const uint8_t>): function for deserialization
+ * - void Pack(std::span<uint8_t>, T&& value): function for
* serialization
*
+ * If possible, the GetTypeString(), GetSize(), and GetSchema() functions should
+ * be marked constexpr. GetTypeString() and GetSchema() may return types other
+ * than std::string_view, as long as the return value is convertible to
+ * std::string_view.
+ *
* If the struct has nested structs, implementations should also meet the
* requirements of HasNestedStruct<T>.
*/
-template <typename T>
-concept StructSerializable =
- requires(std::span<const uint8_t> in, std::span<uint8_t> out, T&& value) {
- typename Struct<typename std::remove_cvref_t<T>>;
- {
- Struct<typename std::remove_cvref_t<T>>::kTypeString
- } -> std::convertible_to<std::string_view>;
- {
- Struct<typename std::remove_cvref_t<T>>::kSize
- } -> std::convertible_to<size_t>;
- {
- Struct<typename std::remove_cvref_t<T>>::kSchema
- } -> std::convertible_to<std::string_view>;
- {
- Struct<typename std::remove_cvref_t<T>>::Unpack(
- in.subspan<0, Struct<typename std::remove_cvref_t<T>>::kSize>())
- } -> std::same_as<typename std::remove_cvref_t<T>>;
- Struct<typename std::remove_cvref_t<T>>::Pack(
- out.subspan<0, Struct<typename std::remove_cvref_t<T>>::kSize>(),
- std::forward<T>(value));
- };
+template <typename T, typename... I>
+concept StructSerializable = requires(std::span<const uint8_t> in,
+ std::span<uint8_t> out, T&& value,
+ const I&... info) {
+ typename Struct<typename std::remove_cvref_t<T>,
+ typename std::remove_cvref_t<I>...>;
+ {
+ Struct<typename std::remove_cvref_t<T>,
+ typename std::remove_cvref_t<I>...>::GetTypeString(info...)
+ } -> std::convertible_to<std::string_view>;
+ {
+ Struct<typename std::remove_cvref_t<T>,
+ typename std::remove_cvref_t<I>...>::GetSize(info...)
+ } -> std::convertible_to<size_t>;
+ {
+ Struct<typename std::remove_cvref_t<T>,
+ typename std::remove_cvref_t<I>...>::GetSchema(info...)
+ } -> std::convertible_to<std::string_view>;
+ {
+ Struct<typename std::remove_cvref_t<T>,
+ typename std::remove_cvref_t<I>...>::Unpack(in, info...)
+ } -> std::same_as<typename std::remove_cvref_t<T>>;
+ Struct<typename std::remove_cvref_t<T>,
+ typename std::remove_cvref_t<I>...>::Pack(out, std::forward<T>(value),
+ info...);
+};
/**
* Specifies that a type is capable of in-place raw struct deserialization.
@@ -84,11 +98,12 @@
* wpi::Struct<T> static member `void UnpackInto(T*, std::span<const uint8_t>)`
* to update the pointed-to T with the contents of the span.
*/
-template <typename T>
+template <typename T, typename... I>
concept MutableStructSerializable =
- StructSerializable<T> && requires(T* out, std::span<const uint8_t> in) {
- Struct<typename std::remove_cvref_t<T>>::UnpackInto(
- out, in.subspan<0, Struct<typename std::remove_cvref_t<T>>::kSize>());
+ StructSerializable<T, I...> &&
+ requires(T* out, std::span<const uint8_t> in, const I&... info) {
+ Struct<typename std::remove_cvref_t<T>,
+ typename std::remove_cvref_t<I>...>::UnpackInto(out, in, info...);
};
/**
@@ -100,11 +115,13 @@
* fn)` (or equivalent) and call ForEachNestedStruct<Type> on each nested struct
* type.
*/
-template <typename T>
+template <typename T, typename... I>
concept HasNestedStruct =
- StructSerializable<T> &&
- requires(function_ref<void(std::string_view, std::string_view)> fn) {
- Struct<typename std::remove_cvref_t<T>>::ForEachNested(fn);
+ StructSerializable<T, I...> &&
+ requires(function_ref<void(std::string_view, std::string_view)> fn,
+ const I&... info) {
+ Struct<typename std::remove_cvref_t<T>,
+ typename std::remove_cvref_t<I>...>::ForEachNested(fn, info...);
};
/**
@@ -112,11 +129,14 @@
*
* @tparam T object type
* @param data raw struct data
+ * @param info optional struct type info
* @return Deserialized object
*/
-template <StructSerializable T>
-inline T UnpackStruct(std::span<const uint8_t, Struct<T>::kSize> data) {
- return Struct<T>::Unpack(data);
+template <typename T, typename... I>
+ requires StructSerializable<T, I...>
+inline T UnpackStruct(std::span<const uint8_t> data, const I&... info) {
+ using S = Struct<T, typename std::remove_cvref_t<I>...>;
+ return S::Unpack(data, info...);
}
/**
@@ -126,11 +146,14 @@
* @tparam T object type
* @tparam Offset starting offset
* @param data raw struct data
+ * @param info optional struct type info
* @return Deserialized object
*/
-template <StructSerializable T, size_t Offset>
-inline T UnpackStruct(std::span<const uint8_t> data) {
- return Struct<T>::Unpack(data.template subspan<Offset, Struct<T>::kSize>());
+template <typename T, size_t Offset, typename... I>
+ requires StructSerializable<T, I...>
+inline T UnpackStruct(std::span<const uint8_t> data, const I&... info) {
+ using S = Struct<T, typename std::remove_cvref_t<I>...>;
+ return S::Unpack(data.subspan(Offset), info...);
}
/**
@@ -138,12 +161,14 @@
*
* @param data struct storage (mutable, output)
* @param value object
+ * @param info optional struct type info
*/
-template <StructSerializable T>
-inline void PackStruct(
- std::span<uint8_t, Struct<typename std::remove_cvref_t<T>>::kSize> data,
- T&& value) {
- Struct<typename std::remove_cvref_t<T>>::Pack(data, std::forward<T>(value));
+template <typename T, typename... I>
+ requires StructSerializable<T, I...>
+inline void PackStruct(std::span<uint8_t> data, T&& value, const I&... info) {
+ using S = Struct<typename std::remove_cvref_t<T>,
+ typename std::remove_cvref_t<I>...>;
+ S::Pack(data, std::forward<T>(value), info...);
}
/**
@@ -153,13 +178,14 @@
* @tparam Offset starting offset
* @param data struct storage (mutable, output)
* @param value object
+ * @param info optional struct type info
*/
-template <size_t Offset, StructSerializable T>
-inline void PackStruct(std::span<uint8_t> data, T&& value) {
- Struct<typename std::remove_cvref_t<T>>::Pack(
- data.template subspan<Offset,
- Struct<typename std::remove_cvref_t<T>>::kSize>(),
- std::forward<T>(value));
+template <size_t Offset, typename T, typename... I>
+ requires StructSerializable<T, I...>
+inline void PackStruct(std::span<uint8_t> data, T&& value, const I&... info) {
+ using S = Struct<typename std::remove_cvref_t<T>,
+ typename std::remove_cvref_t<I>...>;
+ S::Pack(data.subspan(Offset), std::forward<T>(value), info...);
}
/**
@@ -167,14 +193,17 @@
*
* @param out object (output)
* @param data raw struct data
+ * @param info optional struct type info
*/
-template <StructSerializable T>
-inline void UnpackStructInto(T* out,
- std::span<const uint8_t, Struct<T>::kSize> data) {
- if constexpr (MutableStructSerializable<T>) {
- Struct<T>::UnpackInto(out, data);
+template <typename T, typename... I>
+ requires StructSerializable<T, I...>
+inline void UnpackStructInto(T* out, std::span<const uint8_t> data,
+ const I&... info) {
+ using S = Struct<T, typename std::remove_cvref_t<I>...>;
+ if constexpr (MutableStructSerializable<T, I...>) {
+ S::UnpackInto(out, data, info...);
} else {
- *out = UnpackStruct<T>(data);
+ *out = UnpackStruct<T>(data, info...);
}
}
@@ -186,14 +215,17 @@
* @tparam Offset starting offset
* @param out object (output)
* @param data raw struct data
+ * @param info optional struct type info
*/
-template <size_t Offset, StructSerializable T>
-inline void UnpackStructInto(T* out, std::span<const uint8_t> data) {
- if constexpr (MutableStructSerializable<T>) {
- Struct<T>::UnpackInto(out,
- data.template subspan<Offset, Struct<T>::kSize>());
+template <size_t Offset, typename T, typename... I>
+ requires StructSerializable<T, I...>
+inline void UnpackStructInto(T* out, std::span<const uint8_t> data,
+ const I&... info) {
+ using S = Struct<T, typename std::remove_cvref_t<I>...>;
+ if constexpr (MutableStructSerializable<T, I...>) {
+ S::UnpackInto(out, data.subspan(Offset), info...);
} else {
- *out = UnpackStruct<T, Offset>(data);
+ *out = UnpackStruct<T, Offset>(data, info...);
}
}
@@ -201,68 +233,116 @@
* Get the type string for a raw struct serializable type
*
* @tparam T serializable type
+ * @param info optional struct type info
* @return type string
*/
-template <StructSerializable T>
-constexpr auto GetStructTypeString() {
- return Struct<T>::kTypeString;
+template <typename T, typename... I>
+ requires StructSerializable<T, I...>
+constexpr auto GetStructTypeString(const I&... info) {
+ using S = Struct<T, typename std::remove_cvref_t<I>...>;
+ return S::GetTypeString(info...);
}
-template <StructSerializable T, size_t N>
-consteval auto MakeStructArrayTypeString() {
- using namespace literals;
- if constexpr (N == std::dynamic_extent) {
- return Concat(
- ct_string<char, std::char_traits<char>, Struct<T>::kTypeString.size()>{
- Struct<T>::kTypeString},
- "[]"_ct_string);
+/**
+ * Get the size for a raw struct serializable type
+ *
+ * @tparam T serializable type
+ * @param info optional struct type info
+ * @return size
+ */
+template <typename T, typename... I>
+ requires StructSerializable<T, I...>
+constexpr size_t GetStructSize(const I&... info) {
+ using S = Struct<T, typename std::remove_cvref_t<I>...>;
+ return S::GetSize(info...);
+}
+
+template <typename T, size_t N, typename... I>
+ requires StructSerializable<T, I...>
+constexpr auto MakeStructArrayTypeString(const I&... info) {
+ using S = Struct<T, typename std::remove_cvref_t<I>...>;
+ if constexpr (sizeof...(I) == 0 &&
+ is_constexpr([&] { S::GetTypeString(info...); })) {
+ constexpr auto typeString = S::GetTypeString(info...);
+ using namespace literals;
+ if constexpr (N == std::dynamic_extent) {
+ return Concat(
+ ct_string<char, std::char_traits<char>, typeString.size()>{
+ typeString},
+ "[]"_ct_string);
+ } else {
+ return Concat(
+ ct_string<char, std::char_traits<char>, typeString.size()>{
+ typeString},
+ "["_ct_string, NumToCtString<N>(), "]"_ct_string);
+ }
} else {
- return Concat(
- ct_string<char, std::char_traits<char>, Struct<T>::kTypeString.size()>{
- Struct<T>::kTypeString},
- "["_ct_string, NumToCtString<N>(), "]"_ct_string);
+ if constexpr (N == std::dynamic_extent) {
+ return fmt::format("{}[]", S::GetTypeString(info...));
+ } else {
+ return fmt::format("{}[{}]", S::GetTypeString(info...), N);
+ }
}
}
-template <StructSerializable T, size_t N>
-consteval auto MakeStructArraySchema() {
- using namespace literals;
- if constexpr (N == std::dynamic_extent) {
- return Concat(
- ct_string<char, std::char_traits<char>, Struct<T>::kSchema.size()>{
- Struct<T>::kSchema},
- "[]"_ct_string);
+template <typename T, size_t N, typename... I>
+ requires StructSerializable<T, I...>
+constexpr auto MakeStructArraySchema(const I&... info) {
+ using S = Struct<T, typename std::remove_cvref_t<I>...>;
+ if constexpr (sizeof...(I) == 0 &&
+ is_constexpr([&] { S::GetSchema(info...); })) {
+ constexpr auto schema = S::GetSchema(info...);
+ using namespace literals;
+ if constexpr (N == std::dynamic_extent) {
+ return Concat(
+ ct_string<char, std::char_traits<char>, schema.size()>{schema},
+ "[]"_ct_string);
+ } else {
+ return Concat(
+ ct_string<char, std::char_traits<char>, schema.size()>{schema},
+ "["_ct_string, NumToCtString<N>(), "]"_ct_string);
+ }
} else {
- return Concat(
- ct_string<char, std::char_traits<char>, Struct<T>::kSchema.size()>{
- Struct<T>::kSchema},
- "["_ct_string, NumToCtString<N>(), "]"_ct_string);
+ if constexpr (N == std::dynamic_extent) {
+ return fmt::format("{}[]", S::GetSchema(info...));
+ } else {
+ return fmt::format("{}[{}]", S::GetSchema(info...), N);
+ }
}
}
-template <StructSerializable T>
-constexpr std::string_view GetStructSchema() {
- return Struct<T>::kSchema;
+template <typename T, typename... I>
+ requires StructSerializable<T, I...>
+constexpr std::string_view GetStructSchema(const I&... info) {
+ using S = Struct<T, typename std::remove_cvref_t<I>...>;
+ return S::GetSchema(info...);
}
-template <StructSerializable T>
-constexpr std::span<const uint8_t> GetStructSchemaBytes() {
- return {reinterpret_cast<const uint8_t*>(Struct<T>::kSchema.data()),
- Struct<T>::kSchema.size()};
+template <typename T, typename... I>
+ requires StructSerializable<T, I...>
+constexpr std::span<const uint8_t> GetStructSchemaBytes(const I&... info) {
+ using S = Struct<T, typename std::remove_cvref_t<I>...>;
+ auto schema = S::GetSchema(info...);
+ return {reinterpret_cast<const uint8_t*>(schema.data()), schema.size()};
}
-template <StructSerializable T>
+template <typename T, typename... I>
+ requires StructSerializable<T, I...>
void ForEachStructSchema(
- std::invocable<std::string_view, std::string_view> auto fn) {
- if constexpr (HasNestedStruct<T>) {
- Struct<T>::ForEachNested(fn);
+ std::invocable<std::string_view, std::string_view> auto fn,
+ const I&... info) {
+ using S = Struct<typename std::remove_cvref_t<T>,
+ typename std::remove_cvref_t<I>...>;
+ if constexpr (HasNestedStruct<T, I...>) {
+ S::ForEachNested(fn, info...);
}
- fn(Struct<T>::kTypeString, Struct<T>::kSchema);
+ fn(S::GetTypeString(info...), S::GetSchema(info...));
}
-template <StructSerializable T>
+template <typename T, typename... I>
+ requires StructSerializable<T, I...>
class StructArrayBuffer {
- using S = Struct<T>;
+ using S = Struct<T, I...>;
public:
StructArrayBuffer() = default;
@@ -281,25 +361,26 @@
std::convertible_to<std::ranges::range_value_t<U>, T> &&
#endif
std::invocable<F, std::span<const uint8_t>>
- void Write(U&& data, F&& func) {
- if ((std::size(data) * S::kSize) < 256) {
+ void Write(U&& data, F&& func, const I&... info) {
+ auto size = S::GetSize(info...);
+ if ((std::size(data) * size) < 256) {
// use the stack
uint8_t buf[256];
auto out = buf;
for (auto&& val : data) {
- S::Pack(std::span<uint8_t, S::kSize>{out, out + S::kSize},
- std::forward<decltype(val)>(val));
- out += S::kSize;
+ S::Pack(std::span<uint8_t>{std::to_address(out), size},
+ std::forward<decltype(val)>(val), info...);
+ out += size;
}
func(std::span<uint8_t>{buf, out});
} else {
std::scoped_lock lock{m_mutex};
- m_buf.resize(std::size(data) * S::kSize);
+ m_buf.resize(std::size(data) * size);
auto out = m_buf.begin();
for (auto&& val : data) {
- S::Pack(std::span<uint8_t, S::kSize>{out, out + S::kSize},
- std::forward<decltype(val)>(val));
- out += S::kSize;
+ S::Pack(std::span<uint8_t>{std::to_address(out), size},
+ std::forward<decltype(val)>(val), info...);
+ out += size;
}
func(m_buf);
}
@@ -313,38 +394,49 @@
/**
* Raw struct support for fixed-size arrays of other structs.
*/
-template <StructSerializable T, size_t N>
-struct Struct<std::array<T, N>> {
- static constexpr auto kTypeString = MakeStructArrayTypeString<T, N>();
- static constexpr size_t kSize = N * Struct<T>::kSize;
- static constexpr auto kSchema = MakeStructArraySchema<T, N>();
- static std::array<T, N> Unpack(std::span<const uint8_t, kSize> data) {
+template <typename T, size_t N, typename... I>
+ requires StructSerializable<T, I...>
+struct Struct<std::array<T, N>, I...> {
+ static constexpr auto GetTypeString(const I&... info) {
+ return MakeStructArrayTypeString<T, N>(info...);
+ }
+ static constexpr size_t GetSize(const I&... info) {
+ return N * GetStructSize<T>(info...);
+ }
+ static constexpr auto GetSchema(const I&... info) {
+ return MakeStructArraySchema<T, N>(info...);
+ }
+ static std::array<T, N> Unpack(std::span<const uint8_t> data,
+ const I&... info) {
+ auto size = GetStructSize<T>(info...);
std::array<T, N> result;
for (size_t i = 0; i < N; ++i) {
- result[i] = UnpackStruct<T, 0>(data);
- data = data.subspan(Struct<T>::kSize);
+ result[i] = UnpackStruct<T, 0>(data, info...);
+ data = data.subspan(size);
}
return result;
}
- static void Pack(std::span<uint8_t, kSize> data,
- std::span<const T, N> values) {
+ static void Pack(std::span<uint8_t> data, std::span<const T, N> values,
+ const I&... info) {
+ auto size = GetStructSize<T>(info...);
std::span<uint8_t> unsizedData = data;
for (auto&& val : values) {
- PackStruct<0>(unsizedData, val);
- unsizedData = unsizedData.subspan(Struct<T>::kSize);
+ PackStruct(unsizedData, val, info...);
+ unsizedData = unsizedData.subspan(size);
}
}
- static void UnpackInto(std::array<T, N>* out,
- std::span<const uint8_t, kSize> data) {
- UnpackInto(std::span{*out}, data);
+ static void UnpackInto(std::array<T, N>* out, std::span<const uint8_t> data,
+ const I&... info) {
+ UnpackInto(std::span{*out}, data, info...);
}
// alternate span-based function
- static void UnpackInto(std::span<T, N> out,
- std::span<const uint8_t, kSize> data) {
+ static void UnpackInto(std::span<T, N> out, std::span<const uint8_t> data,
+ const I&... info) {
+ auto size = GetStructSize<T>(info...);
std::span<const uint8_t> unsizedData = data;
for (size_t i = 0; i < N; ++i) {
- UnpackStructInto<0, T>(&out[i], unsizedData);
- unsizedData = unsizedData.subspan(Struct<T>::kSize);
+ UnpackStructInto(&out[i], unsizedData, info...);
+ unsizedData = unsizedData.subspan(size);
}
}
};
@@ -355,11 +447,11 @@
*/
template <>
struct Struct<bool> {
- static constexpr std::string_view kTypeString = "struct:bool";
- static constexpr size_t kSize = 1;
- static constexpr std::string_view kSchema = "bool value";
- static bool Unpack(std::span<const uint8_t, 1> data) { return data[0]; }
- static void Pack(std::span<uint8_t, 1> data, bool value) {
+ static constexpr std::string_view GetTypeString() { return "struct:bool"; }
+ static constexpr size_t GetSize() { return 1; }
+ static constexpr std::string_view GetSchema() { return "bool value"; }
+ static bool Unpack(std::span<const uint8_t> data) { return data[0]; }
+ static void Pack(std::span<uint8_t> data, bool value) {
data[0] = static_cast<char>(value ? 1 : 0);
}
};
@@ -370,13 +462,11 @@
*/
template <>
struct Struct<uint8_t> {
- static constexpr std::string_view kTypeString = "struct:uint8";
- static constexpr size_t kSize = 1;
- static constexpr std::string_view kSchema = "uint8 value";
- static uint8_t Unpack(std::span<const uint8_t, 1> data) { return data[0]; }
- static void Pack(std::span<uint8_t, 1> data, uint8_t value) {
- data[0] = value;
- }
+ static constexpr std::string_view GetTypeString() { return "struct:uint8"; }
+ static constexpr size_t GetSize() { return 1; }
+ static constexpr std::string_view GetSchema() { return "uint8 value"; }
+ static uint8_t Unpack(std::span<const uint8_t> data) { return data[0]; }
+ static void Pack(std::span<uint8_t> data, uint8_t value) { data[0] = value; }
};
/**
@@ -385,9 +475,9 @@
*/
template <>
struct Struct<int8_t> {
- static constexpr std::string_view kTypeString = "struct:int8";
- static constexpr size_t kSize = 1;
- static constexpr std::string_view kSchema = "int8 value";
+ static constexpr std::string_view GetTypeString() { return "struct:int8"; }
+ static constexpr size_t GetSize() { return 1; }
+ static constexpr std::string_view GetSchema() { return "int8 value"; }
static int8_t Unpack(std::span<const uint8_t, 1> data) { return data[0]; }
static void Pack(std::span<uint8_t, 1> data, int8_t value) {
data[0] = value;
@@ -400,13 +490,13 @@
*/
template <>
struct Struct<uint16_t> {
- static constexpr std::string_view kTypeString = "struct:uint16";
- static constexpr size_t kSize = 2;
- static constexpr std::string_view kSchema = "uint16 value";
- static uint16_t Unpack(std::span<const uint8_t, 2> data) {
+ static constexpr std::string_view GetTypeString() { return "struct:uint16"; }
+ static constexpr size_t GetSize() { return 2; }
+ static constexpr std::string_view GetSchema() { return "uint16 value"; }
+ static uint16_t Unpack(std::span<const uint8_t> data) {
return support::endian::read16le(data.data());
}
- static void Pack(std::span<uint8_t, 2> data, uint16_t value) {
+ static void Pack(std::span<uint8_t> data, uint16_t value) {
support::endian::write16le(data.data(), value);
}
};
@@ -417,13 +507,13 @@
*/
template <>
struct Struct<int16_t> {
- static constexpr std::string_view kTypeString = "struct:int16";
- static constexpr size_t kSize = 2;
- static constexpr std::string_view kSchema = "int16 value";
- static int16_t Unpack(std::span<const uint8_t, 2> data) {
+ static constexpr std::string_view GetTypeString() { return "struct:int16"; }
+ static constexpr size_t GetSize() { return 2; }
+ static constexpr std::string_view GetSchema() { return "int16 value"; }
+ static int16_t Unpack(std::span<const uint8_t> data) {
return support::endian::read16le(data.data());
}
- static void Pack(std::span<uint8_t, 2> data, int16_t value) {
+ static void Pack(std::span<uint8_t> data, int16_t value) {
support::endian::write16le(data.data(), value);
}
};
@@ -434,13 +524,13 @@
*/
template <>
struct Struct<uint32_t> {
- static constexpr std::string_view kTypeString = "struct:uint32";
- static constexpr size_t kSize = 4;
- static constexpr std::string_view kSchema = "uint32 value";
- static uint32_t Unpack(std::span<const uint8_t, 4> data) {
+ static constexpr std::string_view GetTypeString() { return "struct:uint32"; }
+ static constexpr size_t GetSize() { return 4; }
+ static constexpr std::string_view GetSchema() { return "uint32 value"; }
+ static uint32_t Unpack(std::span<const uint8_t> data) {
return support::endian::read32le(data.data());
}
- static void Pack(std::span<uint8_t, 4> data, uint32_t value) {
+ static void Pack(std::span<uint8_t> data, uint32_t value) {
support::endian::write32le(data.data(), value);
}
};
@@ -451,13 +541,13 @@
*/
template <>
struct Struct<int32_t> {
- static constexpr std::string_view kTypeString = "struct:int32";
- static constexpr size_t kSize = 4;
- static constexpr std::string_view kSchema = "int32 value";
- static int32_t Unpack(std::span<const uint8_t, 4> data) {
+ static constexpr std::string_view GetTypeString() { return "struct:int32"; }
+ static constexpr size_t GetSize() { return 4; }
+ static constexpr std::string_view GetSchema() { return "int32 value"; }
+ static int32_t Unpack(std::span<const uint8_t> data) {
return support::endian::read32le(data.data());
}
- static void Pack(std::span<uint8_t, 4> data, int32_t value) {
+ static void Pack(std::span<uint8_t> data, int32_t value) {
support::endian::write32le(data.data(), value);
}
};
@@ -468,13 +558,13 @@
*/
template <>
struct Struct<uint64_t> {
- static constexpr std::string_view kTypeString = "struct:uint64";
- static constexpr size_t kSize = 8;
- static constexpr std::string_view kSchema = "uint64 value";
- static uint64_t Unpack(std::span<const uint8_t, 8> data) {
+ static constexpr std::string_view GetTypeString() { return "struct:uint64"; }
+ static constexpr size_t GetSize() { return 8; }
+ static constexpr std::string_view GetSchema() { return "uint64 value"; }
+ static uint64_t Unpack(std::span<const uint8_t> data) {
return support::endian::read64le(data.data());
}
- static void Pack(std::span<uint8_t, 8> data, uint64_t value) {
+ static void Pack(std::span<uint8_t> data, uint64_t value) {
support::endian::write64le(data.data(), value);
}
};
@@ -485,13 +575,13 @@
*/
template <>
struct Struct<int64_t> {
- static constexpr std::string_view kTypeString = "struct:int64";
- static constexpr size_t kSize = 8;
- static constexpr std::string_view kSchema = "int64 value";
- static int64_t Unpack(std::span<const uint8_t, 8> data) {
+ static constexpr std::string_view GetTypeString() { return "struct:int64"; }
+ static constexpr size_t GetSize() { return 8; }
+ static constexpr std::string_view GetSchema() { return "int64 value"; }
+ static int64_t Unpack(std::span<const uint8_t> data) {
return support::endian::read64le(data.data());
}
- static void Pack(std::span<uint8_t, 8> data, int64_t value) {
+ static void Pack(std::span<uint8_t> data, int64_t value) {
support::endian::write64le(data.data(), value);
}
};
@@ -502,13 +592,13 @@
*/
template <>
struct Struct<float> {
- static constexpr std::string_view kTypeString = "struct:float";
- static constexpr size_t kSize = 4;
- static constexpr std::string_view kSchema = "float value";
- static float Unpack(std::span<const uint8_t, 4> data) {
+ static constexpr std::string_view GetTypeString() { return "struct:float"; }
+ static constexpr size_t GetSize() { return 4; }
+ static constexpr std::string_view GetSchema() { return "float value"; }
+ static float Unpack(std::span<const uint8_t> data) {
return bit_cast<float>(support::endian::read32le(data.data()));
}
- static void Pack(std::span<uint8_t, 4> data, float value) {
+ static void Pack(std::span<uint8_t> data, float value) {
support::endian::write32le(data.data(), bit_cast<uint32_t>(value));
}
};
@@ -519,13 +609,13 @@
*/
template <>
struct Struct<double> {
- static constexpr std::string_view kTypeString = "struct:double";
- static constexpr size_t kSize = 8;
- static constexpr std::string_view kSchema = "double value";
- static double Unpack(std::span<const uint8_t, 8> data) {
+ static constexpr std::string_view GetTypeString() { return "struct:double"; }
+ static constexpr size_t GetSize() { return 8; }
+ static constexpr std::string_view GetSchema() { return "double value"; }
+ static double Unpack(std::span<const uint8_t> data) {
return bit_cast<double>(support::endian::read64le(data.data()));
}
- static void Pack(std::span<uint8_t, 8> data, double value) {
+ static void Pack(std::span<uint8_t> data, double value) {
support::endian::write64le(data.data(), bit_cast<uint64_t>(value));
}
};
diff --git a/wpiutil/src/main/native/include/wpi/timestamp.h b/wpiutil/src/main/native/include/wpi/timestamp.h
index c232481..4d61055 100644
--- a/wpiutil/src/main/native/include/wpi/timestamp.h
+++ b/wpiutil/src/main/native/include/wpi/timestamp.h
@@ -8,15 +8,13 @@
#include <stdint.h>
#ifdef __cplusplus
-extern "C" {
+#include <memory> // NOLINT
+
#endif
-/**
- * Initialize the on-Rio Now() implementation to use the FPGA timestamp.
- * No effect on non-Rio platforms. This is called by HAL_Initialize() and
- * thus should generally not be called by user code.
- */
-void WPI_Impl_SetupNowRio(void);
+#ifdef __cplusplus
+extern "C" {
+#endif
/**
* De-initialize the on-Rio Now() implementation. No effect on non-Rio
@@ -63,11 +61,31 @@
namespace impl {
/**
+ * Initialize the on-Rio Now() implementation to use the desktop timestamp.
+ * No effect on non-Rio platforms. This should only be used for testing
+ * purposes if the HAL is not available.
+ */
+void SetupNowDefaultOnRio();
+
+/**
* Initialize the on-Rio Now() implementation to use the FPGA timestamp.
* No effect on non-Rio platforms. This is called by HAL_Initialize() and
* thus should generally not be called by user code.
*/
-void SetupNowRio();
+#ifdef __FRC_ROBORIO__
+template <typename T>
+void SetupNowRio(void* chipObjectLibrary, std::unique_ptr<T> hmbObject);
+#else
+template <typename T>
+inline void SetupNowRio(void*, std::unique_ptr<T>) {}
+#endif
+
+/**
+ * Initialize the on-Rio Now() implementation to use the FPGA timestamp.
+ * No effect on non-Rio platforms. This take an FPGA session that has
+ * already been initialized, and is used from LabVIEW.
+ */
+void SetupNowRio(uint32_t session);
/**
* De-initialize the on-Rio Now() implementation. No effect on non-Rio
diff --git a/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/core.h b/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/core.h
index 1fe1388..915d895 100644
--- a/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/core.h
+++ b/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/core.h
@@ -1435,7 +1435,7 @@
// Only map owning types because mapping views can be unsafe.
template <typename T, typename U = format_as_t<T>,
FMT_ENABLE_IF(std::is_arithmetic<U>::value)>
- FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) {
+ FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(map(U())) {
return map(format_as(val));
}
@@ -1459,13 +1459,13 @@
!is_string<U>::value && !is_char<U>::value &&
!is_named_arg<U>::value &&
!std::is_arithmetic<format_as_t<U>>::value)>
- FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(this->do_map(val)) {
+ FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(do_map(val)) {
return do_map(val);
}
template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg)
- -> decltype(this->map(named_arg.value)) {
+ -> decltype(map(named_arg.value)) {
return map(named_arg.value);
}
diff --git a/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/MemoryBuffer.cpp b/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/MemoryBuffer.cpp
index 72cdf09..3ee53a2 100644
--- a/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/MemoryBuffer.cpp
+++ b/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/MemoryBuffer.cpp
@@ -230,6 +230,7 @@
fs::file_t f, std::string_view bufferName, std::error_code& ec) {
constexpr size_t ChunkSize = 4096 * 4;
SmallVector<uint8_t, ChunkSize> buffer;
+ uint64_t size = 0;
#ifdef _WIN32
DWORD readBytes;
#else
@@ -237,23 +238,23 @@
#endif
// Read into Buffer until we hit EOF.
do {
- size_t prevSize = buffer.size();
- buffer.resize_for_overwrite(prevSize + ChunkSize);
+ buffer.resize_for_overwrite(size + ChunkSize);
#ifdef _WIN32
- if (!ReadFile(f, buffer.begin() + prevSize, ChunkSize, &readBytes,
- nullptr)) {
+ if (!ReadFile(f, buffer.begin() + size, ChunkSize, &readBytes, nullptr)) {
ec = mapWindowsError(GetLastError());
return nullptr;
}
#else
- readBytes = sys::RetryAfterSignal(-1, ::read, f, buffer.begin() + prevSize,
+ readBytes = sys::RetryAfterSignal(-1, ::read, f, buffer.begin() + size,
ChunkSize);
if (readBytes == -1) {
ec = std::error_code(errno, std::generic_category());
return nullptr;
}
#endif
+ size += readBytes;
} while (readBytes != 0);
+ buffer.truncate(size);
return GetMemBufferCopyImpl(buffer, bufferName, ec);
}
@@ -370,7 +371,7 @@
// If this not a file or a block device (e.g. it's a named pipe
// or character device), we can't mmap it, so error out.
- if (status.st_mode != S_IFREG && status.st_mode != S_IFBLK) {
+ if (!S_ISREG(status.st_mode) && !S_ISBLK(status.st_mode)) {
ec = make_error_code(errc::invalid_argument);
return nullptr;
}
@@ -433,7 +434,7 @@
// If this not a file or a block device (e.g. it's a named pipe
// or character device), we can't trust the size. Create the memory
// buffer by copying off the stream.
- if (status.st_mode != S_IFREG && status.st_mode != S_IFBLK) {
+ if (!S_ISREG(status.st_mode) && !S_ISBLK(status.st_mode)) {
return GetMemoryBufferForStream(f, filename, ec);
}
diff --git a/wpiutil/src/main/native/thirdparty/llvm/include/wpi/MemoryBuffer.h b/wpiutil/src/main/native/thirdparty/llvm/include/wpi/MemoryBuffer.h
index 7907c07..b5eaea4 100644
--- a/wpiutil/src/main/native/thirdparty/llvm/include/wpi/MemoryBuffer.h
+++ b/wpiutil/src/main/native/thirdparty/llvm/include/wpi/MemoryBuffer.h
@@ -62,6 +62,11 @@
std::span<const uint8_t> GetBuffer() const { return {begin(), end()}; }
+ std::span<const char> GetCharBuffer() const {
+ return {reinterpret_cast<const char*>(begin()),
+ reinterpret_cast<const char*>(end())};
+ }
+
/// Return an identifier for this buffer, typically the filename it was read
/// from.
virtual std::string_view GetBufferIdentifier() const {
@@ -145,6 +150,10 @@
uint8_t* begin() { return const_cast<uint8_t*>(MemoryBuffer::begin()); }
uint8_t* end() { return const_cast<uint8_t*>(MemoryBuffer::end()); }
std::span<uint8_t> GetBuffer() { return {begin(), end()}; }
+ std::span<char> GetCharBuffer() const {
+ return {reinterpret_cast<char*>(const_cast<uint8_t*>(begin())),
+ reinterpret_cast<char*>(const_cast<uint8_t*>(end()))};
+ }
static std::unique_ptr<WritableMemoryBuffer> GetFile(
std::string_view filename, std::error_code& ec, int64_t fileSize = -1);
@@ -196,6 +205,10 @@
uint8_t* begin() { return const_cast<uint8_t*>(MemoryBuffer::begin()); }
uint8_t* end() { return const_cast<uint8_t*>(MemoryBuffer::end()); }
std::span<uint8_t> GetBuffer() { return {begin(), end()}; }
+ std::span<char> GetCharBuffer() const {
+ return {reinterpret_cast<char*>(const_cast<uint8_t*>(begin())),
+ reinterpret_cast<char*>(const_cast<uint8_t*>(end()))};
+ }
static std::unique_ptr<WriteThroughMemoryBuffer> GetFile(
std::string_view filename, std::error_code& ec, int64_t fileSize = -1);
diff --git a/wpiutil/src/main/native/thirdparty/llvm/include/wpi/type_traits.h b/wpiutil/src/main/native/thirdparty/llvm/include/wpi/type_traits.h
index d74fd0f..5166f2a 100644
--- a/wpiutil/src/main/native/thirdparty/llvm/include/wpi/type_traits.h
+++ b/wpiutil/src/main/native/thirdparty/llvm/include/wpi/type_traits.h
@@ -76,6 +76,11 @@
} // end namespace detail
+// https://stackoverflow.com/questions/55288555/c-check-if-statement-can-be-evaluated-constexpr
+template<class Lambda, int=(Lambda{}(), 0)>
+constexpr bool is_constexpr(Lambda) { return true; }
+constexpr bool is_constexpr(...) { return false; }
+
} // end namespace wpi
#endif // WPIUTIL_WPI_TYPE_TRAITS_H
diff --git a/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/coded_stream.h b/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/coded_stream.h
index 6c0dd4a..a102cec 100644
--- a/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/coded_stream.h
+++ b/wpiutil/src/main/native/thirdparty/protobuf/include/google/protobuf/io/coded_stream.h
@@ -681,7 +681,14 @@
if (PROTOBUF_PREDICT_FALSE(end_ - ptr < static_cast<int>(size))) {
return WriteRawFallback(data, size, ptr);
}
+#if __GNUC__ >= 13
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstringop-overflow="
+#endif // __GNUC__ >= 13
std::memcpy(ptr, data, size);
+#if __GNUC__ >= 13
+#pragma GCC diagnostic pop
+#endif // __GNUC__ >= 13
return ptr + size;
}
// Writes the buffer specified by data, size to the stream. Possibly by
diff --git a/wpiutil/src/main/native/thirdparty/protobuf/src/unknown_field_set.cpp b/wpiutil/src/main/native/thirdparty/protobuf/src/unknown_field_set.cpp
index 74c358e..c058735 100644
--- a/wpiutil/src/main/native/thirdparty/protobuf/src/unknown_field_set.cpp
+++ b/wpiutil/src/main/native/thirdparty/protobuf/src/unknown_field_set.cpp
@@ -96,9 +96,16 @@
if (fields_.empty()) {
fields_ = std::move(other->fields_);
} else {
+#if __GNUC__ >= 13
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstringop-overflow="
+#endif // __GNUC__ >= 13
fields_.insert(fields_.end(),
std::make_move_iterator(other->fields_.begin()),
std::make_move_iterator(other->fields_.end()));
+#if __GNUC__ >= 13
+#pragma GCC diagnostic pop
+#endif // __GNUC__ >= 13
}
other->fields_.clear();
}
diff --git a/wpiutil/src/printlog/java/printlog/PrintLog.java b/wpiutil/src/printlog/java/printlog/PrintLog.java
index e9339b6..b3d972b 100644
--- a/wpiutil/src/printlog/java/printlog/PrintLog.java
+++ b/wpiutil/src/printlog/java/printlog/PrintLog.java
@@ -10,9 +10,9 @@
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.InputMismatchException;
+import java.util.List;
import java.util.Map;
public final class PrintLog {
@@ -133,11 +133,11 @@
} else if ("boolean".equals(entry.type)) {
System.out.println(" " + record.getBoolean());
} else if ("double[]".equals(entry.type)) {
- System.out.println(" " + Arrays.asList(record.getDoubleArray()));
+ System.out.println(" " + List.of(record.getDoubleArray()));
} else if ("int64[]".equals(entry.type)) {
- System.out.println(" " + Arrays.asList(record.getIntegerArray()));
+ System.out.println(" " + List.of(record.getIntegerArray()));
} else if ("string[]".equals(entry.type)) {
- System.out.println(" " + Arrays.asList(record.getStringArray()));
+ System.out.println(" " + List.of(record.getStringArray()));
}
} catch (InputMismatchException ex) {
System.out.println(" invalid");
diff --git a/wpiutil/src/test/java/edu/wpi/first/util/CircularBufferTest.java b/wpiutil/src/test/java/edu/wpi/first/util/CircularBufferTest.java
index 7972754..a040ba5 100644
--- a/wpiutil/src/test/java/edu/wpi/first/util/CircularBufferTest.java
+++ b/wpiutil/src/test/java/edu/wpi/first/util/CircularBufferTest.java
@@ -21,7 +21,7 @@
@Test
void addFirstTest() {
- CircularBuffer queue = new CircularBuffer(8);
+ var queue = new CircularBuffer<Double>(8);
for (double value : m_values) {
queue.addFirst(value);
@@ -34,7 +34,7 @@
@Test
void addLastTest() {
- CircularBuffer queue = new CircularBuffer(8);
+ var queue = new CircularBuffer<Double>(8);
for (double value : m_values) {
queue.addLast(value);
@@ -47,7 +47,7 @@
@Test
void pushPopTest() {
- CircularBuffer queue = new CircularBuffer(3);
+ var queue = new CircularBuffer<Double>(3);
// Insert three elements into the buffer
queue.addLast(1.0);
@@ -91,22 +91,20 @@
@Test
void resetTest() {
- CircularBuffer queue = new CircularBuffer(5);
+ var queue = new CircularBuffer<Double>(5);
for (int i = 0; i < 6; i++) {
- queue.addLast(i);
+ queue.addLast((double) i);
}
queue.clear();
- for (int i = 0; i < 5; i++) {
- assertEquals(0.0, queue.get(i), 0.00005);
- }
+ assertEquals(0, queue.size());
}
@Test
void resizeTest() {
- CircularBuffer queue = new CircularBuffer(5);
+ var queue = new CircularBuffer<Double>(5);
/* Buffer contains {1, 2, 3, _, _}
* ^ front
diff --git a/wpiutil/src/test/java/edu/wpi/first/util/DoubleCircularBufferTest.java b/wpiutil/src/test/java/edu/wpi/first/util/DoubleCircularBufferTest.java
new file mode 100644
index 0000000..58a39f4
--- /dev/null
+++ b/wpiutil/src/test/java/edu/wpi/first/util/DoubleCircularBufferTest.java
@@ -0,0 +1,213 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class DoubleCircularBufferTest {
+ private final double[] m_values = {
+ 751.848, 766.366, 342.657, 234.252, 716.126, 132.344, 445.697, 22.727, 421.125, 799.913
+ };
+ private final double[] m_addFirstOut = {
+ 799.913, 421.125, 22.727, 445.697, 132.344, 716.126, 234.252, 342.657
+ };
+ private final double[] m_addLastOut = {
+ 342.657, 234.252, 716.126, 132.344, 445.697, 22.727, 421.125, 799.913
+ };
+
+ @Test
+ void addFirstTest() {
+ var queue = new DoubleCircularBuffer(8);
+
+ for (double value : m_values) {
+ queue.addFirst(value);
+ }
+
+ for (int i = 0; i < m_addFirstOut.length; i++) {
+ assertEquals(m_addFirstOut[i], queue.get(i), 0.00005);
+ }
+ }
+
+ @Test
+ void addLastTest() {
+ var queue = new DoubleCircularBuffer(8);
+
+ for (double value : m_values) {
+ queue.addLast(value);
+ }
+
+ for (int i = 0; i < m_addLastOut.length; i++) {
+ assertEquals(m_addLastOut[i], queue.get(i), 0.00005);
+ }
+ }
+
+ @Test
+ void pushPopTest() {
+ var queue = new DoubleCircularBuffer(3);
+
+ // Insert three elements into the buffer
+ queue.addLast(1.0);
+ queue.addLast(2.0);
+ queue.addLast(3.0);
+
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+ assertEquals(3.0, queue.get(2), 0.00005);
+
+ /*
+ * The buffer is full now, so pushing subsequent elements will overwrite the
+ * front-most elements.
+ */
+
+ queue.addLast(4.0); // Overwrite 1 with 4
+
+ // The buffer now contains 2, 3, and 4
+ assertEquals(2.0, queue.get(0), 0.00005);
+ assertEquals(3.0, queue.get(1), 0.00005);
+ assertEquals(4.0, queue.get(2), 0.00005);
+
+ queue.addLast(5.0); // Overwrite 2 with 5
+
+ // The buffer now contains 3, 4, and 5
+ assertEquals(3.0, queue.get(0), 0.00005);
+ assertEquals(4.0, queue.get(1), 0.00005);
+ assertEquals(5.0, queue.get(2), 0.00005);
+
+ assertEquals(5.0, queue.removeLast(), 0.00005); // 5 is removed
+
+ // The buffer now contains 3 and 4
+ assertEquals(3.0, queue.get(0), 0.00005);
+ assertEquals(4.0, queue.get(1), 0.00005);
+
+ assertEquals(3.0, queue.removeFirst(), 0.00005); // 3 is removed
+
+ // Leaving only one element with value == 4
+ assertEquals(4.0, queue.get(0), 0.00005);
+ }
+
+ @Test
+ void resetTest() {
+ var queue = new DoubleCircularBuffer(5);
+
+ for (int i = 0; i < 6; i++) {
+ queue.addLast(i);
+ }
+
+ queue.clear();
+
+ for (int i = 0; i < 5; i++) {
+ assertEquals(0.0, queue.get(i), 0.00005);
+ }
+ }
+
+ @Test
+ void resizeTest() {
+ var queue = new DoubleCircularBuffer(5);
+
+ /* Buffer contains {1, 2, 3, _, _}
+ * ^ front
+ */
+ queue.addLast(1.0);
+ queue.addLast(2.0);
+ queue.addLast(3.0);
+
+ queue.resize(2);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.resize(5);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.clear();
+
+ /* Buffer contains {_, 1, 2, 3, _}
+ * ^ front
+ */
+ queue.addLast(0.0);
+ queue.addLast(1.0);
+ queue.addLast(2.0);
+ queue.addLast(3.0);
+ queue.removeFirst();
+
+ queue.resize(2);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.resize(5);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.clear();
+
+ /* Buffer contains {_, _, 1, 2, 3}
+ * ^ front
+ */
+ queue.addLast(0.0);
+ queue.addLast(0.0);
+ queue.addLast(1.0);
+ queue.addLast(2.0);
+ queue.addLast(3.0);
+ queue.removeFirst();
+ queue.removeFirst();
+
+ queue.resize(2);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.resize(5);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.clear();
+
+ /* Buffer contains {3, _, _, 1, 2}
+ * ^ front
+ */
+ queue.addLast(3.0);
+ queue.addFirst(2.0);
+ queue.addFirst(1.0);
+
+ queue.resize(2);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.resize(5);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.clear();
+
+ /* Buffer contains {2, 3, _, _, 1}
+ * ^ front
+ */
+ queue.addLast(2.0);
+ queue.addLast(3.0);
+ queue.addFirst(1.0);
+
+ queue.resize(2);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ queue.resize(5);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+
+ // Test addLast() after resize
+ queue.addLast(3.0);
+ assertEquals(1.0, queue.get(0), 0.00005);
+ assertEquals(2.0, queue.get(1), 0.00005);
+ assertEquals(3.0, queue.get(2), 0.00005);
+
+ // Test addFirst() after resize
+ queue.addFirst(4.0);
+ assertEquals(4.0, queue.get(0), 0.00005);
+ assertEquals(1.0, queue.get(1), 0.00005);
+ assertEquals(2.0, queue.get(2), 0.00005);
+ assertEquals(3.0, queue.get(3), 0.00005);
+ }
+}
diff --git a/wpiutil/src/test/java/edu/wpi/first/util/struct/parser/ParserTest.java b/wpiutil/src/test/java/edu/wpi/first/util/struct/parser/ParserTest.java
index 52ba8da..b985b2a 100644
--- a/wpiutil/src/test/java/edu/wpi/first/util/struct/parser/ParserTest.java
+++ b/wpiutil/src/test/java/edu/wpi/first/util/struct/parser/ParserTest.java
@@ -15,21 +15,21 @@
@Test
void testEmpty() {
Parser p = new Parser("");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertTrue(schema.declarations.isEmpty());
}
@Test
void testEmptySemicolon() {
Parser p = new Parser(";");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertTrue(schema.declarations.isEmpty());
}
@Test
void testSimple() {
Parser p = new Parser("int32 a");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
@@ -40,14 +40,14 @@
@Test
void testSimpleTrailingSemi() {
Parser p = new Parser("int32 a;");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
}
@Test
void testArray() {
Parser p = new Parser("int32 a[2]");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
@@ -58,14 +58,14 @@
@Test
void testArrayTrailingSemi() {
Parser p = new Parser("int32 a[2];");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
}
@Test
void testBitfield() {
Parser p = new Parser("int32 a:2");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
@@ -76,14 +76,14 @@
@Test
void testBitfieldTrailingSemi() {
Parser p = new Parser("int32 a:2;");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
}
@Test
void testEnumKeyword() {
Parser p = new Parser("enum {x=1} int32 a;");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
@@ -95,7 +95,7 @@
@Test
void testEnumNoKeyword() {
Parser p = new Parser("{x=1} int32 a;");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
@@ -107,7 +107,7 @@
@Test
void testEnumNoValues() {
Parser p = new Parser("{} int32 a;");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
@@ -118,7 +118,7 @@
@Test
void testEnumMultipleValues() {
Parser p = new Parser("{x=1,y=-2} int32 a;");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
@@ -131,7 +131,7 @@
@Test
void testEnumTrailingComma() {
Parser p = new Parser("{x=1,y=2,} int32 a;");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
@@ -144,7 +144,7 @@
@Test
void testMultipleNoTrailingSemi() {
Parser p = new Parser("int32 a; int16 b");
- ParsedSchema schema = assertDoesNotThrow(() -> p.parse());
+ ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 2);
assertEquals(schema.declarations.get(0).typeString, "int32");
assertEquals(schema.declarations.get(0).name, "a");
@@ -155,58 +155,55 @@
@Test
void testErrBitfieldArray() {
Parser p = new Parser("int32 a[1]:2");
- assertThrows(ParseException.class, () -> p.parse(), "10: expected ';', got ':'");
+ assertThrows(ParseException.class, p::parse, "10: expected ';', got ':'");
}
@Test
void testErrNoArrayValue() {
Parser p = new Parser("int32 a[]");
- assertThrows(ParseException.class, () -> p.parse(), "8: expected integer, got ']'");
+ assertThrows(ParseException.class, p::parse, "8: expected integer, got ']'");
}
@Test
void testErrNoBitfieldValue() {
Parser p = new Parser("int32 a:");
- assertThrows(ParseException.class, () -> p.parse(), "8: expected integer, got ''");
+ assertThrows(ParseException.class, p::parse, "8: expected integer, got ''");
}
@Test
void testErrNoNameArray() {
Parser p = new Parser("int32 [2]");
- assertThrows(ParseException.class, () -> p.parse(), "6: expected identifier, got '['");
+ assertThrows(ParseException.class, p::parse, "6: expected identifier, got '['");
}
@Test
void testErrNoNameBitField() {
Parser p = new Parser("int32 :2");
- assertThrows(ParseException.class, () -> p.parse(), "6: expected identifier, got ':'");
+ assertThrows(ParseException.class, p::parse, "6: expected identifier, got ':'");
}
@Test
void testNegativeBitField() {
Parser p = new Parser("int32 a:-1");
assertThrows(
- ParseException.class, () -> p.parse(), "8: bitfield width '-1' is not a positive integer");
+ ParseException.class, p::parse, "8: bitfield width '-1' is not a positive integer");
}
@Test
void testNegativeArraySize() {
Parser p = new Parser("int32 a[-1]");
- assertThrows(
- ParseException.class, () -> p.parse(), "8: array size '-1' is not a positive integer");
+ assertThrows(ParseException.class, p::parse, "8: array size '-1' is not a positive integer");
}
@Test
void testZeroBitField() {
Parser p = new Parser("int32 a:0");
- assertThrows(
- ParseException.class, () -> p.parse(), "8: bitfield width '0' is not a positive integer");
+ assertThrows(ParseException.class, p::parse, "8: bitfield width '0' is not a positive integer");
}
@Test
void testZeroArraySize() {
Parser p = new Parser("int32 a[0]");
- assertThrows(
- ParseException.class, () -> p.parse(), "8: array size '0' is not a positive integer");
+ assertThrows(ParseException.class, p::parse, "8: array size '0' is not a positive integer");
}
}
diff --git a/wpiutil/src/test/native/cpp/ArrayTest.cpp b/wpiutil/src/test/native/cpp/ArrayTest.cpp
index f70b201..d3a12a4 100644
--- a/wpiutil/src/test/native/cpp/ArrayTest.cpp
+++ b/wpiutil/src/test/native/cpp/ArrayTest.cpp
@@ -16,20 +16,17 @@
} // namespace
TEST(ArrayTest, CopyableTypeCompiles) {
- constexpr wpi::array<int, 3> arr1{1, 2, 3};
- static_cast<void>(arr1);
+ [[maybe_unused]] constexpr wpi::array<int, 3> arr1{1, 2, 3};
// Test deduction guide
- constexpr wpi::array arr2{1, 2, 3};
- static_cast<void>(arr2);
+ [[maybe_unused]] constexpr wpi::array arr2{1, 2, 3};
}
TEST(ArrayTest, MoveOnlyTypeCompiles) {
- constexpr wpi::array<MoveOnlyType, 3> arr1{MoveOnlyType{}, MoveOnlyType{},
- MoveOnlyType{}};
- static_cast<void>(arr1);
+ [[maybe_unused]] constexpr wpi::array<MoveOnlyType, 3> arr1{
+ MoveOnlyType{}, MoveOnlyType{}, MoveOnlyType{}};
// Test deduction guide
- constexpr wpi::array arr2{MoveOnlyType{}, MoveOnlyType{}, MoveOnlyType{}};
- static_cast<void>(arr2);
+ [[maybe_unused]] constexpr wpi::array arr2{MoveOnlyType{}, MoveOnlyType{},
+ MoveOnlyType{}};
}
diff --git a/wpiutil/src/test/native/cpp/DataLogTest.cpp b/wpiutil/src/test/native/cpp/DataLogTest.cpp
new file mode 100644
index 0000000..d06ad33
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/DataLogTest.cpp
@@ -0,0 +1,249 @@
+// 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 <array>
+
+#include <gtest/gtest.h>
+
+#include "wpi/DataLog.h"
+
+namespace {
+struct ThingA {
+ int x = 0;
+};
+
+struct ThingB {
+ int x = 0;
+};
+
+struct ThingC {
+ int x = 0;
+};
+
+struct Info1 {
+ int info = 0;
+};
+
+struct Info2 {
+ int info = 0;
+};
+} // namespace
+
+template <>
+struct wpi::Struct<ThingA> {
+ static constexpr std::string_view GetTypeString() { return "struct:ThingA"; }
+ static constexpr size_t GetSize() { return 1; }
+ static constexpr std::string_view GetSchema() { return "uint8 value"; }
+ static ThingA Unpack(std::span<const uint8_t> data) {
+ return ThingA{.x = data[0]};
+ }
+ static void Pack(std::span<uint8_t> data, const ThingA& value) {
+ data[0] = value.x;
+ }
+};
+
+template <>
+struct wpi::Struct<ThingB, Info1> {
+ static constexpr std::string_view GetTypeString(const Info1&) {
+ return "struct:ThingB";
+ }
+ static constexpr size_t GetSize(const Info1&) { return 1; }
+ static constexpr std::string_view GetSchema(const Info1&) {
+ return "uint8 value";
+ }
+ static ThingB Unpack(std::span<const uint8_t> data, const Info1&) {
+ return ThingB{.x = data[0]};
+ }
+ static void Pack(std::span<uint8_t> data, const ThingB& value, const Info1&) {
+ data[0] = value.x;
+ }
+};
+
+template <>
+struct wpi::Struct<ThingC> {
+ static constexpr std::string_view GetTypeString() { return "struct:ThingC"; }
+ static constexpr size_t GetSize() { return 1; }
+ static constexpr std::string_view GetSchema() { return "uint8 value"; }
+ static ThingC Unpack(std::span<const uint8_t> data) {
+ return ThingC{.x = data[0]};
+ }
+ static void Pack(std::span<uint8_t> data, const ThingC& value) {
+ data[0] = value.x;
+ }
+};
+
+template <>
+struct wpi::Struct<ThingC, Info1> {
+ static constexpr std::string_view GetTypeString(const Info1&) {
+ return "struct:ThingC";
+ }
+ static constexpr size_t GetSize(const Info1&) { return 1; }
+ static constexpr std::string_view GetSchema(const Info1&) {
+ return "uint8 value";
+ }
+ static ThingC Unpack(std::span<const uint8_t> data, const Info1&) {
+ return ThingC{.x = data[0]};
+ }
+ static void Pack(std::span<uint8_t> data, const ThingC& value, const Info1&) {
+ data[0] = value.x;
+ }
+};
+
+template <>
+struct wpi::Struct<ThingC, Info2> {
+ static constexpr std::string_view GetTypeString(const Info2&) {
+ return "struct:ThingC";
+ }
+ static constexpr size_t GetSize(const Info2&) { return 1; }
+ static constexpr std::string_view GetSchema(const Info2&) {
+ return "uint8 value";
+ }
+ static ThingC Unpack(std::span<const uint8_t> data, const Info2&) {
+ return ThingC{.x = data[0]};
+ }
+ static void Pack(std::span<uint8_t> data, const ThingC& value, const Info2&) {
+ data[0] = value.x;
+ }
+};
+
+static_assert(wpi::StructSerializable<ThingA>);
+static_assert(!wpi::StructSerializable<ThingA, Info1>);
+
+static_assert(!wpi::StructSerializable<ThingB>);
+static_assert(wpi::StructSerializable<ThingB, Info1>);
+static_assert(!wpi::StructSerializable<ThingB, Info2>);
+
+static_assert(wpi::StructSerializable<ThingC>);
+static_assert(wpi::StructSerializable<ThingC, Info1>);
+static_assert(wpi::StructSerializable<ThingC, Info2>);
+
+TEST(DataLogTest, SimpleInt) {
+ std::vector<uint8_t> data;
+ {
+ wpi::log::DataLog log{
+ [&](auto out) { data.insert(data.end(), out.begin(), out.end()); }};
+ int entry = log.Start("test", "int64");
+ log.AppendInteger(entry, 1, 0);
+ }
+ ASSERT_EQ(data.size(), 66u);
+}
+
+TEST(DataLogTest, StructA) {
+ wpi::log::DataLog log{[](auto) {}};
+ [[maybe_unused]] wpi::log::StructLogEntry<ThingA> entry0;
+ wpi::log::StructLogEntry<ThingA> entry{log, "a", 5};
+ entry.Append(ThingA{});
+ entry.Append(ThingA{}, 7);
+}
+
+TEST(DataLogTest, StructArrayA) {
+ wpi::log::DataLog log{[](auto) {}};
+ [[maybe_unused]] wpi::log::StructArrayLogEntry<ThingA> entry0;
+ wpi::log::StructArrayLogEntry<ThingA> entry{log, "a", 5};
+ entry.Append({{ThingA{}, ThingA{}}});
+ entry.Append({{ThingA{}, ThingA{}}}, 7);
+}
+
+TEST(DataLogTest, StructFixedArrayA) {
+ wpi::log::DataLog log{[](auto) {}};
+ [[maybe_unused]] wpi::log::StructArrayLogEntry<std::array<ThingA, 2>> entry0;
+ wpi::log::StructLogEntry<std::array<ThingA, 2>> entry{log, "a", 5};
+ std::array<ThingA, 2> arr;
+ entry.Append(arr);
+ entry.Append(arr, 7);
+}
+
+TEST(DataLogTest, StructB) {
+ wpi::log::DataLog log{[](auto) {}};
+ Info1 info;
+ [[maybe_unused]] wpi::log::StructLogEntry<ThingB, Info1> entry0;
+ wpi::log::StructLogEntry<ThingB, Info1> entry{log, "b", info, 5};
+ entry.Append(ThingB{});
+ entry.Append(ThingB{}, 7);
+}
+
+TEST(DataLogTest, StructArrayB) {
+ wpi::log::DataLog log{[](auto) {}};
+ Info1 info;
+ [[maybe_unused]] wpi::log::StructArrayLogEntry<ThingB, Info1> entry0;
+ wpi::log::StructArrayLogEntry<ThingB, Info1> entry{log, "a", info, 5};
+ entry.Append({{ThingB{}, ThingB{}}});
+ entry.Append({{ThingB{}, ThingB{}}}, 7);
+}
+
+TEST(DataLogTest, StructFixedArrayB) {
+ wpi::log::DataLog log{[](auto) {}};
+ Info1 info;
+ wpi::log::StructLogEntry<std::array<ThingB, 2>, Info1> entry{log, "a", info,
+ 5};
+ std::array<ThingB, 2> arr;
+ entry.Append(arr);
+ entry.Append(arr, 7);
+}
+
+TEST(DataLogTest, StructC) {
+ wpi::log::DataLog log{[](auto) {}};
+ {
+ wpi::log::StructLogEntry<ThingC> entry{log, "c", 5};
+ entry.Append(ThingC{});
+ entry.Append(ThingC{}, 7);
+ }
+ {
+ Info1 info;
+ wpi::log::StructLogEntry<ThingC, Info1> entry{log, "c1", info, 5};
+ entry.Append(ThingC{});
+ entry.Append(ThingC{}, 7);
+ }
+ {
+ Info2 info;
+ wpi::log::StructLogEntry<ThingC, Info2> entry{log, "c2", info, 5};
+ entry.Append(ThingC{});
+ entry.Append(ThingC{}, 7);
+ }
+}
+
+TEST(DataLogTest, StructArrayC) {
+ wpi::log::DataLog log{[](auto) {}};
+ {
+ wpi::log::StructArrayLogEntry<ThingC> entry{log, "c", 5};
+ entry.Append({{ThingC{}, ThingC{}}});
+ entry.Append({{ThingC{}, ThingC{}}}, 7);
+ }
+ {
+ Info1 info;
+ wpi::log::StructArrayLogEntry<ThingC, Info1> entry{log, "c1", info, 5};
+ entry.Append({{ThingC{}, ThingC{}}});
+ entry.Append({{ThingC{}, ThingC{}}}, 7);
+ }
+ {
+ Info2 info;
+ wpi::log::StructArrayLogEntry<ThingC, Info2> entry{log, "c2", info, 5};
+ entry.Append({{ThingC{}, ThingC{}}});
+ entry.Append({{ThingC{}, ThingC{}}}, 7);
+ }
+}
+
+TEST(DataLogTest, StructFixedArrayC) {
+ wpi::log::DataLog log{[](auto) {}};
+ std::array<ThingC, 2> arr;
+ {
+ wpi::log::StructLogEntry<std::array<ThingC, 2>> entry{log, "c", 5};
+ entry.Append(arr);
+ entry.Append(arr, 7);
+ }
+ {
+ Info1 info;
+ wpi::log::StructLogEntry<std::array<ThingC, 2>, Info1> entry{log, "c1",
+ info, 5};
+ entry.Append(arr);
+ entry.Append(arr, 7);
+ }
+ {
+ Info2 info;
+ wpi::log::StructLogEntry<std::array<ThingC, 2>, Info2> entry{log, "c2",
+ info, 5};
+ entry.Append(arr);
+ entry.Append(arr, 7);
+ }
+}
diff --git a/wpiutil/wpiutil-config.cmake.in b/wpiutil/wpiutil-config.cmake.in
index 3f696c8..3926ecb 100644
--- a/wpiutil/wpiutil-config.cmake.in
+++ b/wpiutil/wpiutil-config.cmake.in
@@ -2,6 +2,7 @@
@FILENAME_DEP_REPLACE@
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_dependency(Threads)
+find_dependency(Protobuf)
@FMTLIB_SYSTEM_REPLACE@
if(@USE_SYSTEM_FMTLIB@)
@@ -10,3 +11,6 @@
@FILENAME_DEP_REPLACE@
include(${SELF_DIR}/wpiutil.cmake)
+if(@WITH_JAVA@)
+ include(${SELF_DIR}/wpiutil_jar.cmake)
+endif()