Compare commits

...

152 Commits

Author SHA1 Message Date
Tyler Veness
53b4891a5e [wpilibcintegrationtests] Fix deprecated Preferences usage (#3461) 2021-06-23 21:58:27 -07:00
Prateek Machiraju
646ded9123 [wpimath] Remove incorrect discretization in pose estimators (#3460) 2021-06-23 21:57:52 -07:00
Peter Johnson
ea0b8f48e6 Fix some deprecation warnings due to fmtlib upgrade (#3459) 2021-06-23 21:57:32 -07:00
Prateek Machiraju
2067d7e300 [wpilibjexamples] Add wpimathjni, wpiutiljni to library path (#3455) 2021-06-22 06:33:24 -07:00
Tyler Veness
866571ab41 [wpiutil] Upgrade to fmtlib 8.0.0 (#3457) 2021-06-21 20:57:42 -07:00
Thad House
4e1fa03087 [build] Skip PDB copy on windows build servers (#3458) 2021-06-21 20:56:42 -07:00
Peter Johnson
b45572167d [build] Change CI back to 18.04 docker images (#3456)
This reverts commit d068fb321f (#3420).

The 18.04 docker images now use GCC 8, which was the original reason for
updating.  20.04 uses a much more recent libc, so staying with 18.04
enables more Linux platforms to use the binaries.
2021-06-20 23:03:29 -07:00
Tyler Veness
57a160f1b3 [wpilibc] Fix LiveWindow deprecation warning in RobotBase skeleton template (#3454) 2021-06-19 20:45:56 -07:00
Thad House
29ae8640d9 [HLT] Implement duty cycle cross connect tests (#3453) 2021-06-19 20:11:52 -07:00
Thad House
ee6377e54b [HLT] Add relay and analog cross connects (#3452) 2021-06-19 13:03:58 -07:00
Peter Johnson
b0f1ae7ea3 [build] CMake: Build the HAL even if WITH_CSCORE=OFF (#3449)
Also handle the case of WITH_WPILIB=OFF WITH_SIMULATION_MODULES=ON.
2021-06-19 09:30:49 -07:00
Tyler Veness
7aae2b72dc Replace std::to_string() with fmt::format() (#3451) 2021-06-19 09:30:01 -07:00
Thad House
73fcbbd748 [HLT] Add relay digital cross connect tests (#3450) 2021-06-19 01:21:56 -07:00
Thad House
e7bedde835 [HLT] Add PWM tests that use DMA as the back end (#3447)
Both a decent DMA test, and slightly more reliable PWM test.
2021-06-19 01:21:07 -07:00
Peter Johnson
7253edb1e1 [wpilibc] Timer: Fix deprecated warning (#3446) 2021-06-18 21:00:26 -07:00
Tyler Veness
efa28125c6 [wpilibc] Add message to RobotBase on how to read stacktrace (#3444)
Also make stacktrace info an error instead of a warning in both C++ and Java.
2021-06-17 23:52:48 -07:00
Thad House
9832fcfe14 [hal] Fix DIO direction getter (#3445)
Unset means input, where the code was assuming set means input.
2021-06-17 23:03:00 -07:00
Ryan Hirasaki
49c71f9f2d [wpilibj] Clarify robot quit message (#3364)
Co-authored-by: David Vo <auscompgeek@users.noreply.github.com>
2021-06-16 13:24:06 -07:00
Noam Zaks
791770cf6e [wpimath] Move controller from wpilibj to wpimath (#3439) 2021-06-16 07:45:51 -07:00
Noam Zaks
9ce9188ff6 [wpimath] Add ReportWarning to MathShared (#3441) 2021-06-16 00:52:24 -07:00
Peter Johnson
362066a9b7 [wpilib] Deprecate getInstance() in favor of static functions (#3440)
Co-authored-by: Noam Zaks <imnoamzaks@gmail.com>
2021-06-15 23:06:03 -07:00
Thad House
26ff9371d9 Initial commit of cross connect integration test project (#3434)
Adding as a separate project so current integration tests stay working.
2021-06-14 20:08:11 -07:00
Thad House
4a36f86c81 [hal] Add support for DMA to Java (#3158) 2021-06-14 19:56:42 -07:00
Peter Johnson
85144e47ff [commands] Unbreak build (#3438) 2021-06-14 07:35:21 -07:00
Peter Johnson
b417d961ec Split Sendable into NT and non-NT portions (#3432)
The non-NT portion has been moved to wpiutil.
The NT portion has been moved to ntcore (as NTSendable).

SendableBuilder similarly split and moved.

SendableRegistry moved to wpiutil.

In C++, SendableHelper also moved to wpiutil.

This enables use of Sendable from wpimath and also enables
moving several classes from wpilib to wpimath.
2021-06-13 16:38:05 -07:00
Starlight220
ef4ea84cb5 [commands] Change grouping decorator impl to flatten nested group structures (#3335) 2021-06-13 16:05:14 -07:00
Prateek Machiraju
b422665a3c [examples] Invert right side of drive subsystems (#3437)
The right motors of a DifferentialDrive are no longer automatically
inverted (#3340) so it needs to be done explicitly.
2021-06-13 15:43:16 -07:00
Thad House
186dadf14d [hal] Error if attempting to set DIO output on an input port (#3436) 2021-06-13 15:00:43 -07:00
Tyler Veness
04e64db945 Remove redundant C++ lambda parentheses (NFC) (#3433) 2021-06-12 08:06:45 -07:00
Peter Johnson
f60994ad24 [wpiutil] Rename Java package to edu.wpi.first.util (#3431)
This is more consistent with wpimath being edu.wpi.first.math.
2021-06-12 01:17:09 -07:00
Peter Johnson
cfa1ca96f2 [wpilibc] Make ShuffleboardValue non-copyable (#3430)
This avoids the possibility of it being accidentally sliced by users.
2021-06-11 20:16:35 -07:00
Tyler Veness
4d9ff76433 Fix documentation warnings generated by JavaDoc (NFC) (#3428)
Some C++ Doxygen comments were updated to reflect any wording changes.

See `rg "(@return|@param \w+) TODO" | less` for list of incomplete docs.
2021-06-10 20:46:47 -07:00
Peter Johnson
9e1b7e0464 [build] Fix clang-tidy and clang-format (#3429)
Use the official ubuntu packages on 20.04; don't use ubuntu-latest.
2021-06-10 20:46:21 -07:00
Tyler Veness
a77c6ff3a2 [build] Upgrade clang-format and clang-tidy (NFC) (#3422) 2021-06-10 11:13:09 -07:00
Tyler Veness
099fde97d5 [wpilib] Improve PDP comments (NFC) (#3427)
Also remove HAL doxygen comments from sources;
these functions already had more descriptive comments in their
corresponding headers.
2021-06-10 00:02:51 -07:00
Tyler Veness
f8fc2463ee [wpilibc, wpiutil] Clean up includes (NFC) (#3426) 2021-06-10 00:00:06 -07:00
Tyler Veness
e246b78846 [wpimath] Clean up member initialization in feedforward classes (#3425) 2021-06-09 23:59:31 -07:00
Tyler Veness
c1e128bd5a Disable frivolous PMD warnings and enable PMD in ntcore (#3419)
Some valid warnings like throwing NullPointerException or using a for
loop instead of System.arraycopy() were fixed.

Abstract classes marked with PMD.AbstractClassWithoutAbstractMethod were
made concrete because they already had protected constructors.

Fixes #1697.
2021-06-09 07:01:00 -07:00
Tyler Veness
8284075ee4 Run "Lint and Format" CI job on push as well as pull request (#3412)
This makes the formatter run when pushing to local forks before a pull
request is made.

This is not run on the main branch.
2021-06-08 23:22:40 -07:00
Tyler Veness
f7db09a128 [wpimath] Move C++ filters into filter folder to match Java (#3417) 2021-06-08 21:21:01 -07:00
Tyler Veness
f9c3d54bd1 [wpimath] Reset error covariance in pose estimator ResetPosition() (#3418)
This also fixes a member function name inconsistency between languages
and adds missing documentation to C++'s KalmanFilterLatencyCompensator.

Fixes #3229.
2021-06-08 21:20:43 -07:00
Tyler Veness
0773f4033e [hal] Ensure HAL status variables are initialized to zero (#3421)
HAL functions don't set the status variable on success, so it's possible
to use the status variable in an uninitialized state.
2021-06-08 21:18:59 -07:00
Peter Johnson
d068fb321f [build] Upgrade CI to use 20.04 docker images (#3420) 2021-06-08 12:27:22 -07:00
Peter Johnson
8d054c940c [wpiutil] Remove STLExtras.h
This is a very inefficient header, and it's good to remove to discourage
its use.  Only a handful of use cases remained, and of only array_lengthof.
2021-06-06 21:35:50 -07:00
Peter Johnson
80f1d79218 [wpiutil] Split function_ref to a separate header 2021-06-06 21:35:50 -07:00
Peter Johnson
64f5413253 Use wpi::span instead of wpi::ArrayRef across all libraries (#3414)
- Remove ArrayRef.h
- Add SpanExtras.h for a couple of convenience functions
2021-06-06 19:51:14 -07:00
Peter Johnson
2abbbd9e70 [build] clang-tidy: Remove bugprone-exception-escape (#3415)
This generates warnings for using fmt::print() in main(), which is a
case we're okay with.
2021-06-06 17:47:14 -07:00
Tyler Veness
a5c471af7e [wpimath] Add LQR template specialization for 2x2 system
A differential drive has this dimensionality (2 velocity states and 2
voltage inputs).
2021-06-06 16:45:12 -07:00
Tyler Veness
edd2f0232c [wpimath] Add DARE solver for Q, R, and N with LQR ctor overloads
This is useful for implementing implicit model following.
2021-06-06 16:45:12 -07:00
Peter Johnson
b2c3b2dd8e Use std::string_view and fmtlib across all libraries (#3402)
- Twine, StringRef, Format, and NativeFormatting have been removed
- Logging now uses fmtlib style formatting
- Nearly all uses of wpi::outs/errs have been replaced with fmt::print() or
std::puts()/std::fputs() (for unformatted strings).
- A wpi/fmt/raw_ostream.h header has been added to enable
fmt::print() with wpi::raw_ostream
2021-06-06 16:13:58 -07:00
Peter Johnson
4f1cecb8e7 [wpiutil] Remove Path.h (#3413)
This was missed in the std::filesystem change.
2021-06-06 15:50:28 -07:00
Prateek Machiraju
b336eac343 [build] Publish halsim_ws_core to Maven 2021-06-06 15:04:25 -07:00
Prateek Machiraju
2a09f6fa45 [build] Also build sim modules as static libraries
This allows sim modules to be statically linked into an executable to
create a "perfectly static" simulated desktop program. The entry point
was changed to be unique when building static libraries to avoid symbol
collisions.
2021-06-06 15:04:25 -07:00
Thad House
0e702eb799 [hal] Add a unified PCM object (#3331) 2021-06-05 22:36:39 -07:00
Tyler Veness
dea841103d [wpimath] Add fmtlib formatter overloads for Eigen::Matrix and units (#3409)
This allows using Eigen matrices or units natively with fmt::format() or
fmt::print().
2021-06-05 21:10:41 -07:00
Tyler Veness
82856cf816 [wpiutil] Improve wpi::circular_buffer iterators (#3410)
The implementation of wpi::circular_buffer has been effectively replaced
with a dynamically sized copy of wpi::static_circular_buffer with a
resize() member function.
2021-06-05 21:08:12 -07:00
Tyler Veness
8aecda03ed [wpilib] Fix a documentation typo (#3408)
"indicated" was misspelled.
2021-06-05 13:35:03 -07:00
Thad House
5c817082a0 [wpilib] Remove InterruptableSensorBase and replace with interrupt classes (#2410) 2021-06-05 11:25:21 -07:00
Tyler Veness
15c521a7fe [wpimath] Fix drivetrain system identification (#3406)
The units for angular Kv and Ka were inconsistent with the derivation. A
second factory function overload was added for angular units that uses a
trackwidth to convert to the other form.

Notice how section 15.2 of https://file.tavsys.net/control/controls-engineering-in-frc.pdf
defines the angular feedforward as u = Kv,angular v instead of u = Kv,angular + omega.
The units cancel for elements of A but not B, so just the B matrix was incorrect in our code.

This breaks existing C++ code since the units are part of the function
signature.
2021-06-05 11:22:05 -07:00
Thad House
989de4a1bf [build] Force all linker warnings to be fatal for rio builds (#3407)
This will make sure we catch any bugs for missing runtime dependencies before they become bigger problems.
2021-06-05 11:20:09 -07:00
Tyler Veness
d9eeb45b03 [wpilibc] Add units to Ultrasonic class API (#3403) 2021-06-01 21:54:56 -07:00
Peter Johnson
fe570e000c [wpiutil] Replace llvm filesystem with C++17 filesystem (#3401)
Use ghc::filesystem as fill on older GCC (e.g. RoboRIO).
This can be removed once all GCC platforms have upgraded to 8.1 or later.

File open functionality has been retained from LLVM but moved to "fs" namespace
and tweaked for improved consistency with std::filesystem (e.g. error_code is
passed by reference instead of returned).

Also update WPILibC's Filesystem functions to return std::string.
2021-06-01 21:50:35 -07:00
Tyler Veness
01dc0249de [wpimath] Move SlewRateLimiter from wpilib to wpimath (#3399)
Timer was replaced with wpi::Now() to avoid a dependency on other wpilib
classes.
2021-05-31 10:35:54 -07:00
Tyler Veness
93523d572e [wpilibc] Clean up integration tests (#3400)
The command and shuffleboard integration tests were removed because
their unit tests counterparts already provide adequate coverage. Java
already removed these.
2021-05-31 10:21:34 -07:00
Peter Johnson
4f7a4464df [wpiutil] Rewrite StringExtras for std::string_view (#3394)
Remove unused functions and add StringRef-like convenience functions.
Minimize header dependencies.
2021-05-28 23:42:58 -07:00
Tyler Veness
e09293a15e [wpilibc] Transition C++ classes to units::second_t (#3396)
A lot of these are breaking changes. frc::Timer was replaced with the
contents of frc2::Timer. The others were in-place argument changes or
removing deprecated non-unit overloads.
2021-05-28 22:06:59 -07:00
Prateek Machiraju
827b17a52b [build] Create run tasks for Glass and OutlineViewer (#3397) 2021-05-28 22:04:58 -07:00
Peter Johnson
a610379965 [wpiutil] Avoid MSVC warning on span include (#3393) 2021-05-26 23:14:04 -07:00
Peter Johnson
4e2c3051be [wpilibc] Use std::string_view instead of Twine (#3380)
Use fmtlib where needed for string formatting into std::string_view.
2021-05-26 17:44:18 -07:00
Peter Johnson
50915cb7ed [wpilibc] MotorSafety::GetDescription(): Return std::string (#3390)
This is only called in an error condition, so it's not necessary to over
optimize it.
2021-05-26 07:25:32 -07:00
Tyler Veness
f4e2d26d58 [wpilibc] Move NullDeleter from frc/Base.h to wpi/NullDeleter.h (#3387)
frc/Base.h was also deleted because it's now empty.
2021-05-26 07:24:53 -07:00
Peter Johnson
cb0051ae60 [wpilibc] SimDeviceSim: use fmtlib (#3389)
Also clean up several sim classes to use the channel constructor.
2021-05-26 07:23:13 -07:00
Tyler Veness
a238cec12b [wpiutil] Deprecate wpi::math constants in favor of wpi::numbers (#3383)
The constants were moved from std::math to std::numbers before
ratification in C++20.
2021-05-26 00:09:36 -07:00
Tyler Veness
393bf23c0c [ntcore, cscore, wpiutil] Standardize template impl files on .inc extension (NFC) (#3124) 2021-05-25 22:19:30 -07:00
Tyler Veness
e7d9ba135c [sim] Disable flaky web server integration tests (#3388)
The digital output test sometimes fails on Linux and the digital input
test sometimes fails on macOS.
2021-05-25 20:56:35 -07:00
Tyler Veness
0a0003c110 [wpilibjExamples] Fix name of Java swerve drive pose estimator example (#3382) 2021-05-25 20:55:24 -07:00
Tyler Veness
7e1b27554c [wpilibc] Use default copies and moves when possible (#3381)
The removal of ErrorBase allowed the defaults to be used in more places.
2021-05-25 20:54:39 -07:00
Tyler Veness
fb2a56e2d6 [wpilibc] Remove START_ROBOT_CLASS macro (#3384) 2021-05-25 20:53:26 -07:00
Tyler Veness
84218bfb45 [wpilibc] Remove frc namespace shim (#3385) 2021-05-25 20:52:50 -07:00
Tyler Veness
dd78243406 [wpilibc] Remove C++ compiler version static asserts (#3386)
frc/Base.h isn't the first header included basically anywhere, so the
compiler will fail on C++17 things before the asserts in this header are
processed.
2021-05-25 20:52:23 -07:00
Tyler Veness
484cf9c0e8 [wpimath] Suppress the -Wmaybe-uninitialized warning in Eigen (#3378)
GCC 11 emits a false positive when compiling Eigen and breaks the
build.

Fixes #3363.
2021-05-25 10:05:41 -07:00
Peter Johnson
a04d1b4f97 [wpilibc] DriverStation: Remove ReportError and ReportWarning
Change use cases to directly call FRC_ReportError.
2021-05-25 10:04:32 -07:00
Peter Johnson
831c10bdfc [wpilibc] Errors: Use fmtlib 2021-05-25 10:04:32 -07:00
Peter Johnson
87603e400d [wpiutil] Import fmtlib (#3375)
HEAD as of 5/23/2021 (dd8f38fcbbb6eedecd4d451b03ca7d817e8ae67d).
2021-05-24 23:59:35 -07:00
Peter Johnson
4426216725 [wpiutil] Add ArrayRef/std::span/wpi::span implicit conversions 2021-05-23 15:27:40 -07:00
Peter Johnson
bc15b953b4 [wpiutil] Add std::span implementation
Imported from https://github.com/tcbrindle/span with ifdef's removed
(as we require C++17).
2021-05-23 15:27:40 -07:00
Peter Johnson
6d20b12043 [wpiutil] StringRef, Twine, raw_ostream: Add std::string_view support (#3373) 2021-05-23 15:26:28 -07:00
Peter Johnson
2385c2a430 [wpilibc] Remove Utility.h (#3376)
Change last 2 uses of wpi_assert to throw error instead.
2021-05-23 15:25:08 -07:00
Tyler Veness
87384ea684 [wpilib] Fix PIDController continuous range error calculations (#3170)
The inputs should all be errors, so the range should be symmetric.

Fixes #3168.
Fixes #3304.
2021-05-21 23:52:30 -07:00
Tyler Veness
04dae799a2 [wpimath] Add SimpleMotorFeedforward::Calculate(velocity, nextVelocity) overload (#3183)
This is often more convenient than using the overload with velocity and
acceleration.

Fixes #3160.
2021-05-21 23:44:10 -07:00
Tyler Veness
0768c39036 [wpilib] DifferentialDrive: Remove right side inversion (#3340)
Also refactor drive inverse kinematics into separate functions.
This allows composing them with operations separate from the drive
class.
2021-05-21 22:34:16 -07:00
Tyler Veness
8dd8d4d2d4 [wpimath] Fix redundant nested math package introduced by #3316 (#3368) 2021-05-21 22:29:52 -07:00
Dalton Smith
49b06beedf [examples] Add Field2d to RamseteController example (#3371) 2021-05-21 22:28:29 -07:00
Tyler Veness
4c562a4457 [wpimath] Fix typo in comment of update_eigen.py (#3369)
NonMPL2.h clearly doesn't contain MPL2 code.
2021-05-21 21:39:33 -07:00
Tyler Veness
fdbbf11887 [wpimath] Add script for updating Eigen 2021-05-20 18:52:11 -07:00
Tyler Veness
f1e64b349a [wpimath] Move Eigen unsupported folder into eigeninclude
This fixes relative includes in development versions of Eigen.
2021-05-20 18:52:11 -07:00
Ryan Hirasaki
224f3a05cf [sim] Fix build error when building with GCC 11.1 (#3361) 2021-05-19 14:39:18 -07:00
Peter Johnson
ff56d6861d [wpilibj] Fix SpeedController deprecated warnings (#3360)
set() and other functions also need to be repeated on the MotorController
interface to avoid deprecation warnings from vscode.
2021-05-16 19:13:54 -07:00
sciencewhiz
1873fbefba [examples] Fix Swerve and Mecanum examples (#3359)
Fix encoder allocation and default command.
Fixes #3349
2021-05-15 21:39:00 -07:00
sciencewhiz
80b479e502 [examples] Fix SwerveBot example to use unique encoder ports (#3358)
Fixes #3089
2021-05-15 14:15:18 -07:00
PJ Reiniger
1f7c9adeeb [wpilibjExamples] Fix pose estimator examples (#3356) 2021-05-14 11:10:47 -07:00
Peter Johnson
9ebc3b058d [outlineviewer] Change default size to 600x400 (#3353) 2021-05-11 23:34:16 -07:00
Prateek Machiraju
e21b443a45 [build] Gradle: Make C++ examples runnable (#3348) 2021-05-11 19:54:53 -07:00
Peter Johnson
da590120c4 [wpilibj] Add MotorController.setVoltage default (#3347)
This avoids a vscode deprecation warning.
2021-05-11 19:53:55 -07:00
Peter Johnson
561d53885e [build] Update opencv to 4.5.2, imgui/implot to latest (#3344)
Also update native-utils to 2022.0.0 to start pulling 2022 artifacts.
2021-05-10 18:59:14 -07:00
Peter Johnson
44ad67ca8c [wpilibj] Preferences: Add missing Deprecated annotation (#3343) 2021-05-09 18:20:42 -07:00
Peter Johnson
3fe8fc75aa [wpilibc] Revert "Return reference from GetInstance" (#3342)
This reverts commit a79faace1b.

This change will be superseded in a non-breaking way by changing to static functions and deprecating GetInstance() entirely.
2021-05-09 18:16:07 -07:00
Peter Johnson
3cc2da3328 Merge branch '2022' 2021-05-09 14:15:40 -07:00
Tyler Veness
a3cd90dd71 [wpimath] Fix classpath used by generate_numbers.py (#3339) 2021-05-09 00:01:03 -07:00
Noam Zaks
d6cfdd3bae [wpilib] Preferences: Deprecate Put* in favor of Set* (#3337)
This naming is more consistent with other APIs.

Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2021-05-06 08:25:37 -07:00
Tyler Veness
ba08baabb9 [wpimath] Update Drake DARE solver to v0.29.0 (#3336)
This version incorporated the patch we were manually applying, so we're
synced back up with upstream now except for some minor #include changes
to reduce header bloat.
2021-05-05 09:16:55 -07:00
Tyler Veness
497b712f67 [wpilib] Make IterativeRobotBase::m_period private with getter 2021-05-04 13:41:36 -07:00
Tyler Veness
f00dfed7ac [wpilib] Remove IterativeRobot base class
TimedRobot supersedes it (see commit 81498e6 for reasoning).
2021-05-04 13:41:36 -07:00
Thad House
3c08461685 [hal] Use last error reporting instead of PARAMETER_OUT_OF_RANGE (#3328)
Makes the error messages much more specific to each error.
2021-05-01 13:22:08 -07:00
Tyler Veness
5ef2b4fdc0 [wpilibj] Fix @deprecated warning for SerialPort constructor (#3329)
The javadoc @deprecated tag didn't have corresponding @Deprecated
attribute.
2021-05-01 13:20:53 -07:00
Thad House
23d2326d1d [hal] Report previous allocation location for indexed resource duplicates (#3322) 2021-05-01 10:28:30 -07:00
Thad House
e338f9f190 [build] Fix wpilibc runCpp task (#3327) 2021-05-01 10:26:33 -07:00
Noam Zaks
c8ff626fe2 [wpimath] Move Java classes to edu.wpi.first.math (#3316) 2021-05-01 08:53:30 -07:00
Noam Zaks
4e424d51f4 [wpilibj] DifferentialDrivetrainSim: Rename constants to match the style guide (#3312) 2021-05-01 07:09:23 -07:00
Thad House
6b50323b07 [cscore] Use Lock2DSize if possible for Windows USB cameras (#3326)
Can remove a memory copy in many cases. This also fixes a bug where any mjpeg cameras on windows wouldn't work if the fast path was taken.
2021-05-01 07:07:37 -07:00
Tyler Veness
65c148536d [wpilibc] Fix "control reaches end of non-void function" warning (#3324) 2021-05-01 07:05:21 -07:00
Peter Johnson
f99f62bee4 [wpiutil] uv Handle: Use malloc/free instead of new/delete (#3325)
This avoids asan warnings for deleting a different pointer type.
2021-05-01 07:04:14 -07:00
Tyler Veness
365f5449ca [wpimath] Fix MecanumDriveKinematics (#3266) 2021-04-30 15:50:16 -07:00
Starlight220
ff52f207cc [glass, wpilib] Rewrite Mechanism2d (#3281)
Substantially improves Mechanism2d by moving it to NetworkTables and adding
a robot API to create the mechanism elements, instead of requiring a JSON file.

Co-authored-by: Peter Johnson <johnson.peter@gmail.com>
2021-04-30 13:43:59 -07:00
Tyler Veness
ee0eed143a [wpimath] Add DCMotor factory function for Romi motors (#3319) 2021-04-29 09:59:35 -07:00
Thad House
5127380727 [hal] Add HAL_GetLastError to enable better error messages from HAL calls (#3320)
This uses thread local storage so a full error string can be provided, not just an error code.
2021-04-29 09:56:54 -07:00
Noam Zaks
ced654880c [glass, outlineviewer] Update Mac icons to macOS 11 style (#3313) 2021-04-25 17:30:45 -07:00
Peter Johnson
05ebe93180 Merge branch 'main' into 2022 2021-04-19 18:45:31 -07:00
Peter Johnson
8d961dfd25 [wpilibc] Remove ErrorBase (#3306)
Replace with new exception-based error reporting, consistent with Java.
This also builds stacktraces into the reporting/exceptions.
2021-04-18 20:35:29 -07:00
Peter Johnson
0abf6c9045 [wpilib] Move motor controllers to motorcontrol package (#3302)
Also deprecate SpeedController in favor of motorcontrol.MotorController and
SpeedControllerGroup in favor of motorcontrol.MotorControllerGroup.

The MotorController interface is derived from the SpeedController interface
so that code such as SpeedController x = new VictorSP(1) continues to
compile (just with a warning).

SpeedControllerGroup and MotorControllerGroup are independent classes;
both implement the MotorController interface.
2021-04-17 11:27:16 -07:00
Peter Johnson
b7b178f49c [wpilib] Remove Potentiometer interface 2021-04-13 22:40:55 -07:00
Peter Johnson
687066af3d [wpilib] Remove GyroBase 2021-04-13 22:40:55 -07:00
Peter Johnson
6b168ab0c8 [wpilib] Remove PIDController, PIDOutput, PIDSource
Move them to the old commands vendordep so that PIDCommand and PIDSubsystem
continue to work.

This also removes Filter and LinearDigitalFilter.
2021-04-13 22:40:55 -07:00
Peter Johnson
3abe0b9d49 [cscore] Move java package to edu.wpi.first.cscore (#3294)
This is more consistent with the other Java packages, and also is more
correct, as we own the first.wpi.edu domain but not the full wpi.edu domain.
2021-04-10 11:42:41 -07:00
Peter Johnson
d7fabe81fe [wpilib] Remove RobotDrive (#3295)
This has been deprecated for several years, and its functionality has been
completely superseded by other drive classes (DifferentialDrive et al).
2021-04-10 10:28:32 -07:00
Peter Johnson
1dc81669c2 [wpilib] Remove GearTooth (#3293)
This sensor has had zero usage for many years and was last in the KOP
over a decade ago.  There are much better rotation sensors available,
and it's no longer worth maintaining this class.
2021-04-10 10:28:05 -07:00
Peter Johnson
397e569aaf [ntcore] Remove "using wpi" from nt namespace
This removes the nt::ArrayRef, nt::StringRef, and nt::Twine aliases.
2021-04-08 22:35:28 -07:00
Peter Johnson
79267f9e60 [ntcore] Remove NetworkTable -> nt::NetworkTable shim 2021-04-08 22:35:28 -07:00
Peter Johnson
48ebe5736a [ntcore] Remove deprecated Java interfaces and classes 2021-04-08 22:35:28 -07:00
Peter Johnson
c2064c78b2 [ntcore] Remove deprecated ITable interfaces 2021-04-08 22:35:28 -07:00
Peter Johnson
36608a283b [ntcore] Remove deprecated C++ APIs 2021-04-08 22:35:28 -07:00
Peter Johnson
6137f98eb5 [hal] Rename SimValueCallback2 to SimValueCallback (#3212) 2021-03-21 23:22:04 -07:00
Peter Johnson
a6f6539691 [hal] Move registerSimPeriodic functions to HAL package (#3211)
This enables the static lists to be private.
2021-03-21 23:21:47 -07:00
Peter Johnson
a79faace1b [wpilibc] Return reference from GetInstance (#3247)
Improves consistency across all classes.

Affects Preferences, LiveWindow, and CameraServer.

Old commands Scheduler::GetInstance() was not updated as this is already
deprecated.
2021-03-21 11:13:49 -07:00
Peter Johnson
9550777b9d [wpilib] PWMSpeedController: Use PWM by composition (#3248)
This cleans up the user experience by removing lower-level functions from the
interface.

Also remove MotorSafety from "raw" PWM.
2021-03-21 11:12:49 -07:00
Peter Johnson
160fb740f4 [hal] Use std::lround() instead of adding 0.5 and truncating (#3012) 2021-03-19 14:24:46 -07:00
Peter Johnson
48e9f39513 [wpilibj] Remove wpilibj package CameraServer (#3213) 2021-03-19 13:51:53 -07:00
Peter Johnson
8afa596fdf [wpilib] Remove deprecated Sendable functions and SendableBase (#3210) 2021-03-19 13:41:11 -07:00
Prateek Machiraju
d3e45c297c [wpimath] Make C++ geometry classes immutable (#3249) 2021-03-19 13:38:54 -07:00
Peter Johnson
da96707dca Merge branch 'main' into 2022 2021-03-19 09:22:02 -07:00
Peter Johnson
21624ef273 Add ImGui OutlineViewer (#3220) 2021-03-16 22:05:41 -07:00
Peter Johnson
6e23e1840a [wpilibc] Remove WPILib.h (#3235)
It's been deprecated for several years, is often broken as it's not tested
frequently, and dramatically increases compile times.
2021-03-12 15:58:47 -08:00
1832 changed files with 52966 additions and 46543 deletions

View File

@@ -4,7 +4,6 @@ Checks:
bugprone-copy-constructor-init,
bugprone-dangling-handle,
bugprone-dynamic-static-initializers,
bugprone-exception-escape,
bugprone-forward-declaration-namespace,
bugprone-forwarding-reference-overload,
bugprone-inaccurate-erase,

View File

@@ -10,7 +10,7 @@ jobs:
include:
- os: ubuntu-latest
name: Linux
container: wpilib/roborio-cross-ubuntu:2020-18.04
container: wpilib/roborio-cross-ubuntu:2021-18.04
flags: ""
- os: macos-latest
name: macOS

View File

@@ -1,5 +1,5 @@
name: "Validate Gradle Wrapper"
on: [push, pull_request]
on: [pull_request, push]
jobs:
validation:

View File

@@ -1,11 +1,16 @@
name: Lint and Format
on: [pull_request]
on:
pull_request:
push:
branches-ignore:
- main
jobs:
wpiformat:
name: "wpiformat"
runs-on: ubuntu-latest
container: wpilib/roborio-cross-ubuntu:2021-20.04
steps:
- uses: actions/checkout@v2
- name: Fetch all history and metadata
@@ -18,17 +23,20 @@ jobs:
with:
python-version: 3.8
- name: Install clang-format
run: sudo apt-get update -q && sudo apt-get install -y clang-format-10
run: |
sudo sh -c "echo 'deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -cs)-proposed restricted main multiverse universe' >> /etc/apt/sources.list.d/proposed-repositories.list"
sudo apt-get update -q
sudo apt-get install -y clang-format-12
- name: Install wpiformat
run: pip3 install wpiformat
- name: Run
run: wpiformat -clang 10
run: wpiformat -clang 12
- name: Check Output
run: git --no-pager diff --exit-code HEAD
tidy:
name: "clang-tidy"
runs-on: ubuntu-latest
container: wpilib/roborio-cross-ubuntu:2020-18.04
container: wpilib/roborio-cross-ubuntu:2021-20.04
steps:
- uses: actions/checkout@v2
- name: Fetch all history and metadata
@@ -36,8 +44,15 @@ jobs:
git fetch --prune --unshallow
git checkout -b pr
git branch -f main origin/main
- name: Install clang-format and clang-tidy
run: sudo apt-get update -q && sudo apt-get install -y clang-format-10 clang-tidy-10 python3.8
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install clang-tidy
run: |
sudo sh -c "echo 'deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -cs)-proposed restricted main multiverse universe' >> /etc/apt/sources.list.d/proposed-repositories.list"
sudo apt-get update -q
sudo apt-get install -y clang-tidy-12 clang-format-12
- name: Install wpiformat
run: pip3 install wpiformat
- name: Create compile_commands.json
@@ -45,4 +60,4 @@ jobs:
- name: List changed files
run: wpiformat -list-changed-files
- name: Run clang-tidy
run: wpiformat -clang 10 -no-format -tidy-changed -compile-commands=build-cmake
run: wpiformat -clang 12 -no-format -tidy-changed -compile-commands=build-cmake

View File

@@ -23,6 +23,7 @@ includeOtherLibs {
^cameraserver/
^cscore
^drake/
^fmt/
^hal/
^imgui
^implot

View File

@@ -59,6 +59,7 @@ option(WITH_EXTERNAL_HAL "Use a separately built HAL" OFF)
set(EXTERNAL_HAL_FILE "" CACHE FILEPATH "Location to look for an external HAL CMake File")
# Options for using a package manager (vcpkg) for certain dependencies.
option(USE_VCPKG_FMTLIB "Use vcpkg fmtlib" OFF)
option(USE_VCPKG_LIBUV "Use vcpkg libuv" OFF)
option(USE_VCPKG_EIGEN "Use vcpkg eigen" OFF)
@@ -187,6 +188,12 @@ if (WITH_GUI)
add_subdirectory(imgui)
add_subdirectory(wpigui)
add_subdirectory(glass)
add_subdirectory(outlineviewer)
endif()
if (WITH_WPILIB OR WITH_SIMULATION_MODULES)
set(HAL_DEP_REPLACE ${HAL_DEP_REPLACE_IMPL})
add_subdirectory(hal)
endif()
if (WITH_CSCORE)
@@ -195,9 +202,7 @@ if (WITH_CSCORE)
add_subdirectory(cscore)
add_subdirectory(cameraserver)
if (WITH_WPILIB)
set(HAL_DEP_REPLACE ${HAL_DEP_REPLACE_IMPL})
set(WPILIBC_DEP_REPLACE ${WPILIBC_DEP_REPLACE_IMPL})
add_subdirectory(hal)
add_subdirectory(wpilibj)
add_subdirectory(wpilibc)
add_subdirectory(wpilibNewCommands)

View File

@@ -27,6 +27,7 @@ JSON for Modern C++ wpiutil/src/main/native/include/wpi/json.h
libuv wpiutil/src/main/native/include/uv.h
wpiutil/src/main/native/include/uv/
wpiutil/src/main/native/libuv/
fmtlib wpiutil/src/main/native/fmtlib/
sigslot wpiutil/src/main/native/include/wpi/Signal.h
wpiutil/src/test/native/cpp/sigslot/
tcpsockets wpiutil/src/main/native/cpp/TCP{Stream,Connector,Acceptor}.cpp
@@ -40,6 +41,9 @@ units wpimath/src/main/native/include/units/
Eigen wpimath/src/main/native/eigeninclude/
wpimath/src/main/native/include/unsupported/
StackWalker wpiutil/src/main/native/windows/StackWalker.*
TCB span wpiutil/src/main/native/include/wpi/span.h
wpiutil/src/test/native/cpp/span/
GHC filesystem wpiutil/src/main/native/include/wpi/ghc/
Team 254 Library wpilibj/src/main/java/edu/wpi/first/wpilibj/spline/SplineParameterizer.java
wpilibj/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryParameterizer.java
wpilibc/src/main/native/include/spline/SplineParameterizer.h
@@ -856,3 +860,84 @@ the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What the **** You Want
to Public License, Version 2, as published by the WTFPL Task Force.
See http://www.wtfpl.net/ for more details.
======================
Boost Software License
======================
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
======
fmtlib
======
Copyright (c) 2012 - present, Victor Zverovich
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- Optional exception to the license ---
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into a machine-executable object form of such
source code, you may redistribute such embedded portions in such object form
without including the above copyright and permission notices.
==============
GHC filesystem
==============
Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -5,5 +5,5 @@ repositories {
}
}
dependencies {
implementation "edu.wpi.first:native-utils:2021.1.1"
implementation "edu.wpi.first:native-utils:2022.0.2"
}

View File

@@ -12,6 +12,7 @@ repoRootNameOverride {
}
includeOtherLibs {
^fmt/
^hal/
^networktables/
^opencv2/

View File

@@ -10,8 +10,8 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import edu.wpi.cscore.VideoSource;
import edu.wpi.first.cameraserver.CameraServer;
import edu.wpi.first.cscore.VideoSource;
import edu.wpi.first.networktables.NetworkTableInstance;
import java.io.IOException;
import java.nio.file.Files;
@@ -94,7 +94,6 @@ public final class Main {
}
/** Read configuration file. */
@SuppressWarnings("PMD.CyclomaticComplexity")
public static boolean readConfig() {
// parse file
JsonElement top;
@@ -151,7 +150,7 @@ public final class Main {
/** Start running the camera. */
public static void startCamera(CameraConfig config) {
System.out.println("Starting camera '" + config.name + "' on " + config.path);
VideoSource camera = CameraServer.getInstance().startAutomaticCapture(config.name, config.path);
VideoSource camera = CameraServer.startAutomaticCapture(config.name, config.path);
Gson gson = new GsonBuilder().create();

View File

@@ -4,10 +4,12 @@
#include <cstdio>
#include <string>
#include <string_view>
#include <vector>
#include <networktables/NetworkTableInstance.h>
#include <wpi/StringRef.h>
#include <wpi/StringExtras.h>
#include <wpi/fmt/raw_ostream.h>
#include <wpi/json.h>
#include <wpi/raw_istream.h>
#include <wpi/raw_ostream.h>
@@ -105,7 +107,7 @@ bool ReadConfig() {
try {
j = wpi::json::parse(is);
} catch (const wpi::json::parse_error& e) {
ParseError() << "byte " << e.byte << ": " << e.what() << '\n';
fmt::print(ParseError(), "byte {}: {}\n", e.byte, e.what());
return false;
}
@@ -127,10 +129,9 @@ bool ReadConfig() {
if (j.count("ntmode") != 0) {
try {
auto str = j.at("ntmode").get<std::string>();
wpi::StringRef s(str);
if (s.equals_lower("client")) {
if (wpi::equals_lower(str, "client")) {
server = false;
} else if (s.equals_lower("server")) {
} else if (wpi::equals_lower(str, "server")) {
server = true;
} else {
ParseError() << "could not understand ntmode value '" << str << "'\n";
@@ -156,10 +157,9 @@ bool ReadConfig() {
}
void StartCamera(const CameraConfig& config) {
wpi::outs() << "Starting camera '" << config.name << "' on " << config.path
<< '\n';
auto camera = frc::CameraServer::GetInstance()->StartAutomaticCapture(
config.name, config.path);
fmt::print("Starting camera '{}' on {}\n", config.name, config.path);
auto camera =
frc::CameraServer::StartAutomaticCapture(config.name, config.path);
camera.SetConfigJson(config.config);
}
@@ -178,10 +178,10 @@ int main(int argc, char* argv[]) {
// start NetworkTables
auto ntinst = nt::NetworkTableInstance::GetDefault();
if (server) {
wpi::outs() << "Setting up NetworkTables server\n";
std::puts("Setting up NetworkTables server");
ntinst.StartServer();
} else {
wpi::outs() << "Setting up NetworkTables client for team " << team << '\n';
fmt::print("Setting up NetworkTables client for team {}\n", team);
ntinst.StartClientTeam(team);
}

View File

@@ -4,20 +4,20 @@
package edu.wpi.first.cameraserver;
import edu.wpi.cscore.AxisCamera;
import edu.wpi.cscore.CameraServerJNI;
import edu.wpi.cscore.CvSink;
import edu.wpi.cscore.CvSource;
import edu.wpi.cscore.MjpegServer;
import edu.wpi.cscore.UsbCamera;
import edu.wpi.cscore.VideoEvent;
import edu.wpi.cscore.VideoException;
import edu.wpi.cscore.VideoListener;
import edu.wpi.cscore.VideoMode;
import edu.wpi.cscore.VideoMode.PixelFormat;
import edu.wpi.cscore.VideoProperty;
import edu.wpi.cscore.VideoSink;
import edu.wpi.cscore.VideoSource;
import edu.wpi.first.cscore.AxisCamera;
import edu.wpi.first.cscore.CameraServerJNI;
import edu.wpi.first.cscore.CvSink;
import edu.wpi.first.cscore.CvSource;
import edu.wpi.first.cscore.MjpegServer;
import edu.wpi.first.cscore.UsbCamera;
import edu.wpi.first.cscore.VideoEvent;
import edu.wpi.first.cscore.VideoException;
import edu.wpi.first.cscore.VideoListener;
import edu.wpi.first.cscore.VideoMode;
import edu.wpi.first.cscore.VideoMode.PixelFormat;
import edu.wpi.first.cscore.VideoProperty;
import edu.wpi.first.cscore.VideoSink;
import edu.wpi.first.cscore.VideoSource;
import edu.wpi.first.networktables.EntryListenerFlags;
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableEntry;
@@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* Singleton class for creating and keeping camera servers. Also publishes camera information to
* NetworkTables.
*/
@SuppressWarnings("PMD.UnusedPrivateField")
public final class CameraServer {
public static final int kBasePort = 1181;
@@ -43,7 +44,12 @@ public final class CameraServer {
private static final String kPublishName = "/CameraPublisher";
private static CameraServer server;
/** Get the CameraServer instance. */
/**
* Get the CameraServer instance.
*
* @deprecated Use the static methods
*/
@Deprecated
public static synchronized CameraServer getInstance() {
if (server == null) {
server = new CameraServer();
@@ -51,18 +57,211 @@ public final class CameraServer {
return server;
}
private final AtomicInteger m_defaultUsbDevice;
private String m_primarySourceName;
private final Map<String, VideoSource> m_sources;
private final Map<String, VideoSink> m_sinks;
private final Map<Integer, NetworkTable> m_tables; // indexed by source handle
private static final AtomicInteger m_defaultUsbDevice = new AtomicInteger();
private static String m_primarySourceName;
private static final Map<String, VideoSource> m_sources = new HashMap<>();
private static final Map<String, VideoSink> m_sinks = new HashMap<>();
private static final Map<Integer, NetworkTable> m_tables =
new HashMap<>(); // indexed by source handle
// source handle indexed by sink handle
private final Map<Integer, Integer> m_fixedSources;
private final NetworkTable m_publishTable;
private final VideoListener m_videoListener; // NOPMD
private final int m_tableListener; // NOPMD
private int m_nextPort;
private String[] m_addresses;
private static final Map<Integer, Integer> m_fixedSources = new HashMap<>();
private static final NetworkTable m_publishTable =
NetworkTableInstance.getDefault().getTable(kPublishName);
// We publish sources to NetworkTables using the following structure:
// "/CameraPublisher/{Source.Name}/" - root
// - "source" (string): Descriptive, prefixed with type (e.g. "usb:0")
// - "streams" (string array): URLs that can be used to stream data
// - "description" (string): Description of the source
// - "connected" (boolean): Whether source is connected
// - "mode" (string): Current video mode
// - "modes" (string array): Available video modes
// - "Property/{Property}" - Property values
// - "PropertyInfo/{Property}" - Property supporting information
// Listener for video events
private static final VideoListener m_videoListener =
new VideoListener(
event -> {
switch (event.kind) {
case kSourceCreated:
{
// Create subtable for the camera
NetworkTable table = m_publishTable.getSubTable(event.name);
m_tables.put(event.sourceHandle, table);
table.getEntry("source").setString(makeSourceValue(event.sourceHandle));
table
.getEntry("description")
.setString(CameraServerJNI.getSourceDescription(event.sourceHandle));
table
.getEntry("connected")
.setBoolean(CameraServerJNI.isSourceConnected(event.sourceHandle));
table
.getEntry("streams")
.setStringArray(getSourceStreamValues(event.sourceHandle));
try {
VideoMode mode = CameraServerJNI.getSourceVideoMode(event.sourceHandle);
table.getEntry("mode").setDefaultString(videoModeToString(mode));
table.getEntry("modes").setStringArray(getSourceModeValues(event.sourceHandle));
} catch (VideoException ignored) {
// Do nothing. Let the other event handlers update this if there is an error.
}
break;
}
case kSourceDestroyed:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("source").setString("");
table.getEntry("streams").setStringArray(new String[0]);
table.getEntry("modes").setStringArray(new String[0]);
}
break;
}
case kSourceConnected:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
// update the description too (as it may have changed)
table
.getEntry("description")
.setString(CameraServerJNI.getSourceDescription(event.sourceHandle));
table.getEntry("connected").setBoolean(true);
}
break;
}
case kSourceDisconnected:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("connected").setBoolean(false);
}
break;
}
case kSourceVideoModesUpdated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("modes").setStringArray(getSourceModeValues(event.sourceHandle));
}
break;
}
case kSourceVideoModeChanged:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("mode").setString(videoModeToString(event.mode));
}
break;
}
case kSourcePropertyCreated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
putSourcePropertyValue(table, event, true);
}
break;
}
case kSourcePropertyValueUpdated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
putSourcePropertyValue(table, event, false);
}
break;
}
case kSourcePropertyChoicesUpdated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
try {
String[] choices =
CameraServerJNI.getEnumPropertyChoices(event.propertyHandle);
table
.getEntry("PropertyInfo/" + event.name + "/choices")
.setStringArray(choices);
} catch (VideoException ignored) {
// ignore
}
}
break;
}
case kSinkSourceChanged:
case kSinkCreated:
case kSinkDestroyed:
case kNetworkInterfacesChanged:
{
m_addresses = CameraServerJNI.getNetworkInterfaces();
updateStreamValues();
break;
}
default:
break;
}
},
0x4fff,
true);
private static final int m_tableListener =
NetworkTableInstance.getDefault()
.addEntryListener(
kPublishName + "/",
event -> {
String relativeKey = event.name.substring(kPublishName.length() + 1);
// get source (sourceName/...)
int subKeyIndex = relativeKey.indexOf('/');
if (subKeyIndex == -1) {
return;
}
String sourceName = relativeKey.substring(0, subKeyIndex);
VideoSource source = m_sources.get(sourceName);
if (source == null) {
return;
}
// get subkey
relativeKey = relativeKey.substring(subKeyIndex + 1);
// handle standard names
String propName;
if ("mode".equals(relativeKey)) {
// reset to current mode
event.getEntry().setString(videoModeToString(source.getVideoMode()));
return;
} else if (relativeKey.startsWith("Property/")) {
propName = relativeKey.substring(9);
} else if (relativeKey.startsWith("RawProperty/")) {
propName = relativeKey.substring(12);
} else {
return; // ignore
}
// everything else is a property
VideoProperty property = source.getProperty(propName);
switch (property.getKind()) {
case kNone:
return;
case kBoolean:
// reset to current setting
event.getEntry().setBoolean(property.get() != 0);
return;
case kInteger:
case kEnum:
// reset to current setting
event.getEntry().setDouble(property.get());
return;
case kString:
// reset to current setting
event.getEntry().setString(property.getString());
return;
default:
return;
}
},
EntryListenerFlags.kImmediate | EntryListenerFlags.kUpdate);
private static int m_nextPort = kBasePort;
private static String[] m_addresses = new String[0];
@SuppressWarnings("MissingJavadocMethod")
private static String makeSourceValue(int source) {
@@ -90,8 +289,8 @@ public final class CameraServer {
return "mjpg:http://" + address + ":" + port + "/?action=stream";
}
@SuppressWarnings({"MissingJavadocMethod", "PMD.AvoidUsingHardCodedIP"})
private synchronized String[] getSinkStreamValues(int sink) {
@SuppressWarnings("MissingJavadocMethod")
private static synchronized String[] getSinkStreamValues(int sink) {
// Ignore all but MjpegServer
if (VideoSink.getKindFromInt(CameraServerJNI.getSinkKind(sink)) != VideoSink.Kind.kMjpeg) {
return new String[0];
@@ -120,8 +319,8 @@ public final class CameraServer {
return values.toArray(new String[0]);
}
@SuppressWarnings({"MissingJavadocMethod", "PMD.AvoidUsingHardCodedIP"})
private synchronized String[] getSourceStreamValues(int source) {
@SuppressWarnings("MissingJavadocMethod")
private static synchronized String[] getSourceStreamValues(int source) {
// Ignore all but HttpCamera
if (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source))
!= VideoSource.Kind.kHttp) {
@@ -155,12 +354,8 @@ public final class CameraServer {
return values;
}
@SuppressWarnings({
"MissingJavadocMethod",
"PMD.AvoidUsingHardCodedIP",
"PMD.CyclomaticComplexity"
})
private synchronized void updateStreamValues() {
@SuppressWarnings("MissingJavadocMethod")
private static synchronized void updateStreamValues() {
// Over all the sinks...
for (VideoSink i : m_sinks.values()) {
int sink = i.getHandle();
@@ -247,7 +442,7 @@ public final class CameraServer {
return modeStrings;
}
@SuppressWarnings({"MissingJavadocMethod", "PMD.CyclomaticComplexity"})
@SuppressWarnings("MissingJavadocMethod")
private static void putSourcePropertyValue(NetworkTable table, VideoEvent event, boolean isNew) {
String name;
String infoName;
@@ -304,223 +499,7 @@ public final class CameraServer {
}
}
@SuppressWarnings({
"MissingJavadocMethod",
"PMD.UnusedLocalVariable",
"PMD.ExcessiveMethodLength",
"PMD.NPathComplexity"
})
private CameraServer() {
m_defaultUsbDevice = new AtomicInteger();
m_sources = new HashMap<>();
m_sinks = new HashMap<>();
m_fixedSources = new HashMap<>();
m_tables = new HashMap<>();
m_publishTable = NetworkTableInstance.getDefault().getTable(kPublishName);
m_nextPort = kBasePort;
m_addresses = new String[0];
// We publish sources to NetworkTables using the following structure:
// "/CameraPublisher/{Source.Name}/" - root
// - "source" (string): Descriptive, prefixed with type (e.g. "usb:0")
// - "streams" (string array): URLs that can be used to stream data
// - "description" (string): Description of the source
// - "connected" (boolean): Whether source is connected
// - "mode" (string): Current video mode
// - "modes" (string array): Available video modes
// - "Property/{Property}" - Property values
// - "PropertyInfo/{Property}" - Property supporting information
// Listener for video events
m_videoListener =
new VideoListener(
event -> {
switch (event.kind) {
case kSourceCreated:
{
// Create subtable for the camera
NetworkTable table = m_publishTable.getSubTable(event.name);
m_tables.put(event.sourceHandle, table);
table.getEntry("source").setString(makeSourceValue(event.sourceHandle));
table
.getEntry("description")
.setString(CameraServerJNI.getSourceDescription(event.sourceHandle));
table
.getEntry("connected")
.setBoolean(CameraServerJNI.isSourceConnected(event.sourceHandle));
table
.getEntry("streams")
.setStringArray(getSourceStreamValues(event.sourceHandle));
try {
VideoMode mode = CameraServerJNI.getSourceVideoMode(event.sourceHandle);
table.getEntry("mode").setDefaultString(videoModeToString(mode));
table
.getEntry("modes")
.setStringArray(getSourceModeValues(event.sourceHandle));
} catch (VideoException ignored) {
// Do nothing. Let the other event handlers update this if there is an error.
}
break;
}
case kSourceDestroyed:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("source").setString("");
table.getEntry("streams").setStringArray(new String[0]);
table.getEntry("modes").setStringArray(new String[0]);
}
break;
}
case kSourceConnected:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
// update the description too (as it may have changed)
table
.getEntry("description")
.setString(CameraServerJNI.getSourceDescription(event.sourceHandle));
table.getEntry("connected").setBoolean(true);
}
break;
}
case kSourceDisconnected:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("connected").setBoolean(false);
}
break;
}
case kSourceVideoModesUpdated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table
.getEntry("modes")
.setStringArray(getSourceModeValues(event.sourceHandle));
}
break;
}
case kSourceVideoModeChanged:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("mode").setString(videoModeToString(event.mode));
}
break;
}
case kSourcePropertyCreated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
putSourcePropertyValue(table, event, true);
}
break;
}
case kSourcePropertyValueUpdated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
putSourcePropertyValue(table, event, false);
}
break;
}
case kSourcePropertyChoicesUpdated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
try {
String[] choices =
CameraServerJNI.getEnumPropertyChoices(event.propertyHandle);
table
.getEntry("PropertyInfo/" + event.name + "/choices")
.setStringArray(choices);
} catch (VideoException ignored) {
// ignore
}
}
break;
}
case kSinkSourceChanged:
case kSinkCreated:
case kSinkDestroyed:
case kNetworkInterfacesChanged:
{
m_addresses = CameraServerJNI.getNetworkInterfaces();
updateStreamValues();
break;
}
default:
break;
}
},
0x4fff,
true);
// Listener for NetworkTable events
// We don't currently support changing settings via NT due to
// synchronization issues, so just update to current setting if someone
// else tries to change it.
m_tableListener =
NetworkTableInstance.getDefault()
.addEntryListener(
kPublishName + "/",
event -> {
String relativeKey = event.name.substring(kPublishName.length() + 1);
// get source (sourceName/...)
int subKeyIndex = relativeKey.indexOf('/');
if (subKeyIndex == -1) {
return;
}
String sourceName = relativeKey.substring(0, subKeyIndex);
VideoSource source = m_sources.get(sourceName);
if (source == null) {
return;
}
// get subkey
relativeKey = relativeKey.substring(subKeyIndex + 1);
// handle standard names
String propName;
if ("mode".equals(relativeKey)) {
// reset to current mode
event.getEntry().setString(videoModeToString(source.getVideoMode()));
return;
} else if (relativeKey.startsWith("Property/")) {
propName = relativeKey.substring(9);
} else if (relativeKey.startsWith("RawProperty/")) {
propName = relativeKey.substring(12);
} else {
return; // ignore
}
// everything else is a property
VideoProperty property = source.getProperty(propName);
switch (property.getKind()) {
case kNone:
return;
case kBoolean:
// reset to current setting
event.getEntry().setBoolean(property.get() != 0);
return;
case kInteger:
case kEnum:
// reset to current setting
event.getEntry().setDouble(property.get());
return;
case kString:
// reset to current setting
event.getEntry().setString(property.getString());
return;
default:
return;
}
},
EntryListenerFlags.kImmediate | EntryListenerFlags.kUpdate);
}
private CameraServer() {}
/**
* Start automatically capturing images to send to the dashboard.
@@ -531,8 +510,10 @@ public final class CameraServer {
* <p>The first time this overload is called, it calls {@link #startAutomaticCapture(int)} with
* device 0, creating a camera named "USB Camera 0". Subsequent calls increment the device number
* (e.g. 1, 2, etc).
*
* @return The USB camera capturing images.
*/
public UsbCamera startAutomaticCapture() {
public static UsbCamera startAutomaticCapture() {
UsbCamera camera = startAutomaticCapture(m_defaultUsbDevice.getAndIncrement());
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
return camera;
@@ -545,8 +526,9 @@ public final class CameraServer {
* {dev}".
*
* @param dev The device number of the camera interface
* @return The USB camera capturing images.
*/
public UsbCamera startAutomaticCapture(int dev) {
public static UsbCamera startAutomaticCapture(int dev) {
UsbCamera camera = new UsbCamera("USB Camera " + dev, dev);
startAutomaticCapture(camera);
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
@@ -558,8 +540,9 @@ public final class CameraServer {
*
* @param name The name to give the camera
* @param dev The device number of the camera interface
* @return The USB camera capturing images.
*/
public UsbCamera startAutomaticCapture(String name, int dev) {
public static UsbCamera startAutomaticCapture(String name, int dev) {
UsbCamera camera = new UsbCamera(name, dev);
startAutomaticCapture(camera);
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
@@ -571,8 +554,9 @@ public final class CameraServer {
*
* @param name The name to give the camera
* @param path The device path (e.g. "/dev/video0") of the camera
* @return The USB camera capturing images.
*/
public UsbCamera startAutomaticCapture(String name, String path) {
public static UsbCamera startAutomaticCapture(String name, String path) {
UsbCamera camera = new UsbCamera(name, path);
startAutomaticCapture(camera);
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
@@ -583,8 +567,9 @@ public final class CameraServer {
* Start automatically capturing images to send to the dashboard from an existing camera.
*
* @param camera Camera
* @return The MJPEG server serving images from the given camera.
*/
public MjpegServer startAutomaticCapture(VideoSource camera) {
public static MjpegServer startAutomaticCapture(VideoSource camera) {
addCamera(camera);
MjpegServer server = addServer("serve_" + camera.getName());
server.setSource(camera);
@@ -597,8 +582,9 @@ public final class CameraServer {
* <p>This overload calls {@link #addAxisCamera(String, String)} with name "Axis Camera".
*
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
* @return The Axis camera capturing images.
*/
public AxisCamera addAxisCamera(String host) {
public static AxisCamera addAxisCamera(String host) {
return addAxisCamera("Axis Camera", host);
}
@@ -608,8 +594,9 @@ public final class CameraServer {
* <p>This overload calls {@link #addAxisCamera(String, String[])} with name "Axis Camera".
*
* @param hosts Array of Camera host IPs/DNS names
* @return The Axis camera capturing images.
*/
public AxisCamera addAxisCamera(String[] hosts) {
public static AxisCamera addAxisCamera(String[] hosts) {
return addAxisCamera("Axis Camera", hosts);
}
@@ -618,8 +605,9 @@ public final class CameraServer {
*
* @param name The name to give the camera
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
* @return The Axis camera capturing images.
*/
public AxisCamera addAxisCamera(String name, String host) {
public static AxisCamera addAxisCamera(String name, String host) {
AxisCamera camera = new AxisCamera(name, host);
// Create a passthrough MJPEG server for USB access
startAutomaticCapture(camera);
@@ -632,8 +620,9 @@ public final class CameraServer {
*
* @param name The name to give the camera
* @param hosts Array of Camera host IPs/DNS names
* @return The Axis camera capturing images.
*/
public AxisCamera addAxisCamera(String name, String[] hosts) {
public static AxisCamera addAxisCamera(String name, String[] hosts) {
AxisCamera camera = new AxisCamera(name, hosts);
// Create a passthrough MJPEG server for USB access
startAutomaticCapture(camera);
@@ -645,12 +634,15 @@ public final class CameraServer {
* Adds a virtual camera for switching between two streams. Unlike the other addCamera methods,
* this returns a VideoSink rather than a VideoSource. Calling setSource() on the returned object
* can be used to switch the actual source of the stream.
*
* @param name The name to give the camera
* @return The MJPEG server serving images from the given camera.
*/
public MjpegServer addSwitchedCamera(String name) {
public static MjpegServer addSwitchedCamera(String name) {
// create a dummy CvSource
CvSource source = new CvSource(name, VideoMode.PixelFormat.kMJPEG, 160, 120, 30);
MjpegServer server = startAutomaticCapture(source);
synchronized (this) {
synchronized (CameraServer.class) {
m_fixedSources.put(server.getHandle(), source.getHandle());
}
@@ -663,10 +655,12 @@ public final class CameraServer {
*
* <p>This is only valid to call after a camera feed has been added with startAutomaticCapture()
* or addServer().
*
* @return OpenCV sink for the primary camera feed
*/
public CvSink getVideo() {
public static CvSink getVideo() {
VideoSource source;
synchronized (this) {
synchronized (CameraServer.class) {
if (m_primarySourceName == null) {
throw new VideoException("no camera available");
}
@@ -683,11 +677,12 @@ public final class CameraServer {
* image processing on the roboRIO.
*
* @param camera Camera (e.g. as returned by startAutomaticCapture).
* @return OpenCV sink for the specified camera
*/
public CvSink getVideo(VideoSource camera) {
public static CvSink getVideo(VideoSource camera) {
String name = "opencv_" + camera.getName();
synchronized (this) {
synchronized (CameraServer.class) {
VideoSink sink = m_sinks.get(name);
if (sink != null) {
VideoSink.Kind kind = sink.getKind();
@@ -709,10 +704,11 @@ public final class CameraServer {
* image processing on the roboRIO.
*
* @param name Camera name
* @return OpenCV sink for the specified camera
*/
public CvSink getVideo(String name) {
public static CvSink getVideo(String name) {
VideoSource source;
synchronized (this) {
synchronized (CameraServer.class) {
source = m_sources.get(name);
if (source == null) {
throw new VideoException("could not find camera " + name);
@@ -728,8 +724,9 @@ public final class CameraServer {
* @param name Name to give the stream
* @param width Width of the image being sent
* @param height Height of the image being sent
* @return OpenCV source for the MJPEG stream
*/
public CvSource putVideo(String name, int width, int height) {
public static CvSource putVideo(String name, int width, int height) {
CvSource source = new CvSource(name, VideoMode.PixelFormat.kMJPEG, width, height, 30);
startAutomaticCapture(source);
return source;
@@ -739,10 +736,11 @@ public final class CameraServer {
* Adds a MJPEG server at the next available port.
*
* @param name Server name
* @return The MJPEG server
*/
public MjpegServer addServer(String name) {
public static MjpegServer addServer(String name) {
int port;
synchronized (this) {
synchronized (CameraServer.class) {
port = m_nextPort;
m_nextPort++;
}
@@ -753,8 +751,10 @@ public final class CameraServer {
* Adds a MJPEG server.
*
* @param name Server name
* @param port Server port
* @return The MJPEG server
*/
public MjpegServer addServer(String name, int port) {
public static MjpegServer addServer(String name, int port) {
MjpegServer server = new MjpegServer(name, port);
addServer(server);
return server;
@@ -765,8 +765,8 @@ public final class CameraServer {
*
* @param server Server
*/
public void addServer(VideoSink server) {
synchronized (this) {
public static void addServer(VideoSink server) {
synchronized (CameraServer.class) {
m_sinks.put(server.getName(), server);
}
}
@@ -776,8 +776,8 @@ public final class CameraServer {
*
* @param name Server name
*/
public void removeServer(String name) {
synchronized (this) {
public static void removeServer(String name) {
synchronized (CameraServer.class) {
m_sinks.remove(name);
}
}
@@ -787,9 +787,11 @@ public final class CameraServer {
*
* <p>This is only valid to call after a camera feed has been added with startAutomaticCapture()
* or addServer().
*
* @return The server for the primary camera feed
*/
public VideoSink getServer() {
synchronized (this) {
public static VideoSink getServer() {
synchronized (CameraServer.class) {
if (m_primarySourceName == null) {
throw new VideoException("no camera available");
}
@@ -801,9 +803,10 @@ public final class CameraServer {
* Gets a server by name.
*
* @param name Server name
* @return The server
*/
public VideoSink getServer(String name) {
synchronized (this) {
public static VideoSink getServer(String name) {
synchronized (CameraServer.class) {
return m_sinks.get(name);
}
}
@@ -813,9 +816,9 @@ public final class CameraServer {
*
* @param camera Camera
*/
public void addCamera(VideoSource camera) {
public static void addCamera(VideoSource camera) {
String name = camera.getName();
synchronized (this) {
synchronized (CameraServer.class) {
if (m_primarySourceName == null) {
m_primarySourceName = name;
}
@@ -828,8 +831,8 @@ public final class CameraServer {
*
* @param name Camera name
*/
public void removeCamera(String name) {
synchronized (this) {
public static void removeCamera(String name) {
synchronized (CameraServer.class) {
m_sources.remove(name);
}
}

View File

@@ -9,7 +9,11 @@ public final class CameraServerSharedStore {
private CameraServerSharedStore() {}
/** get the CameraServerShared object. */
/**
* Get the CameraServerShared object.
*
* @return The CameraServerSharedObject
*/
public static synchronized CameraServerShared getCameraServerShared() {
if (cameraServerShared == null) {
cameraServerShared =
@@ -36,7 +40,11 @@ public final class CameraServerSharedStore {
return cameraServerShared;
}
/** set the CameraServerShared object. */
/**
* Set the CameraServerShared object.
*
* @param shared The CameraServerShared object.
*/
public static synchronized void setCameraServerShared(CameraServerShared shared) {
cameraServerShared = shared;
}

View File

@@ -17,6 +17,8 @@ public interface VisionPipeline {
/**
* Processes the image input and sets the result objects. Implementations should make these
* objects accessible.
*
* @param image The image to process.
*/
void process(Mat image);
}

View File

@@ -4,9 +4,9 @@
package edu.wpi.first.vision;
import edu.wpi.cscore.CvSink;
import edu.wpi.cscore.VideoSource;
import edu.wpi.first.cameraserver.CameraServerSharedStore;
import edu.wpi.first.cscore.CvSink;
import edu.wpi.first.cscore.VideoSource;
import org.opencv.core.Mat;
/**

View File

@@ -4,7 +4,7 @@
package edu.wpi.first.vision;
import edu.wpi.cscore.VideoSource;
import edu.wpi.first.cscore.VideoSource;
/**
* A vision thread is a special thread that runs a vision pipeline. It is a <i>daemon</i> thread; it

View File

@@ -13,7 +13,7 @@
* implements VisionRunner.Listener&lt;MyFindTotePipeline&gt; {
*
* // A USB camera connected to the roboRIO.
* private {@link edu.wpi.cscore.VideoSource VideoSource} usbCamera;
* private {@link edu.wpi.first.cscore.VideoSource VideoSource} usbCamera;
*
* // A vision pipeline. This could be handwritten or generated by GRIP.
* // This has to implement {@link edu.wpi.first.vision.VisionPipeline}.
@@ -44,7 +44,7 @@
*
* {@literal @}Override
* public void robotInit() {
* usbCamera = CameraServer.getInstance().startAutomaticCapture(0);
* usbCamera = CameraServer.startAutomaticCapture(0);
* findTotePipeline = new MyFindTotePipeline();
* findToteThread = new VisionThread(usbCamera, findTotePipeline, this);
* }

View File

@@ -7,14 +7,14 @@
#include <atomic>
#include <vector>
#include <fmt/format.h>
#include <networktables/NetworkTable.h>
#include <networktables/NetworkTableInstance.h>
#include <wpi/DenseMap.h>
#include <wpi/ManagedStatic.h>
#include <wpi/SmallString.h>
#include <wpi/StringExtras.h>
#include <wpi/StringMap.h>
#include <wpi/mutex.h>
#include <wpi/raw_ostream.h>
#include "cameraserver/CameraServerShared.h"
#include "ntcore_cpp.h"
@@ -23,8 +23,9 @@ using namespace frc;
static constexpr char const* kPublishName = "/CameraPublisher";
struct CameraServer::Impl {
Impl();
namespace {
struct Instance {
Instance();
std::shared_ptr<nt::NetworkTable> GetSourceTable(CS_Source source);
std::vector<std::string> GetSinkStreamValues(CS_Sink sink);
std::vector<std::string> GetSourceStreamValues(CS_Source source);
@@ -41,35 +42,36 @@ struct CameraServer::Impl {
nt::NetworkTableInstance::GetDefault().GetTable(kPublishName)};
cs::VideoListener m_videoListener;
int m_tableListener;
int m_nextPort;
int m_nextPort{CameraServer::kBasePort};
std::vector<std::string> m_addresses;
};
} // namespace
CameraServer* CameraServer::GetInstance() {
struct Creator {
static void* call() { return new CameraServer{}; }
};
struct Deleter {
static void call(void* ptr) { delete static_cast<CameraServer*>(ptr); }
};
static wpi::ManagedStatic<CameraServer, Creator, Deleter> instance;
return &(*instance);
static Instance& GetInstance() {
static Instance instance;
return instance;
}
static wpi::StringRef MakeSourceValue(CS_Source source,
wpi::SmallVectorImpl<char>& buf) {
CameraServer* CameraServer::GetInstance() {
::GetInstance();
static CameraServer instance;
return &instance;
}
static std::string_view MakeSourceValue(CS_Source source,
wpi::SmallVectorImpl<char>& buf) {
CS_Status status = 0;
buf.clear();
switch (cs::GetSourceKind(source, &status)) {
case CS_SOURCE_USB: {
wpi::StringRef prefix{"usb:"};
std::string_view prefix{"usb:"};
buf.append(prefix.begin(), prefix.end());
auto path = cs::GetUsbCameraPath(source, &status);
buf.append(path.begin(), path.end());
break;
}
case CS_SOURCE_HTTP: {
wpi::StringRef prefix{"ip:"};
std::string_view prefix{"ip:"};
buf.append(prefix.begin(), prefix.end());
auto urls = cs::GetHttpCameraUrls(source, &status);
if (!urls.empty()) {
@@ -83,22 +85,19 @@ static wpi::StringRef MakeSourceValue(CS_Source source,
return "unknown:";
}
return wpi::StringRef{buf.begin(), buf.size()};
return {buf.begin(), buf.size()};
}
static std::string MakeStreamValue(const wpi::Twine& address, int port) {
return ("mjpg:http://" + address + wpi::Twine(':') + wpi::Twine(port) +
"/?action=stream")
.str();
static std::string MakeStreamValue(std::string_view address, int port) {
return fmt::format("mjpg:http://{}:{}/?action=stream", address, port);
}
std::shared_ptr<nt::NetworkTable> CameraServer::Impl::GetSourceTable(
CS_Source source) {
std::shared_ptr<nt::NetworkTable> Instance::GetSourceTable(CS_Source source) {
std::scoped_lock lock(m_mutex);
return m_tables.lookup(source);
}
std::vector<std::string> CameraServer::Impl::GetSinkStreamValues(CS_Sink sink) {
std::vector<std::string> Instance::GetSinkStreamValues(CS_Sink sink) {
CS_Status status = 0;
// Ignore all but MjpegServer
@@ -130,8 +129,7 @@ std::vector<std::string> CameraServer::Impl::GetSinkStreamValues(CS_Sink sink) {
return values;
}
std::vector<std::string> CameraServer::Impl::GetSourceStreamValues(
CS_Source source) {
std::vector<std::string> Instance::GetSourceStreamValues(CS_Source source) {
CS_Status status = 0;
// Ignore all but HttpCamera
@@ -165,7 +163,7 @@ std::vector<std::string> CameraServer::Impl::GetSourceStreamValues(
return values;
}
void CameraServer::Impl::UpdateStreamValues() {
void Instance::UpdateStreamValues() {
std::scoped_lock lock(m_mutex);
// Over all the sinks...
for (const auto& i : m_sinks) {
@@ -229,12 +227,8 @@ static std::string PixelFormatToString(int pixelFormat) {
}
static std::string VideoModeToString(const cs::VideoMode& mode) {
std::string rv;
wpi::raw_string_ostream oss{rv};
oss << mode.width << "x" << mode.height;
oss << " " << PixelFormatToString(mode.pixelFormat) << " ";
oss << mode.fps << " fps";
return oss.str();
return fmt::format("{}x{} {} {} fps", mode.width, mode.height,
PixelFormatToString(mode.pixelFormat), mode.fps);
}
static std::vector<std::string> GetSourceModeValues(int source) {
@@ -248,23 +242,20 @@ static std::vector<std::string> GetSourceModeValues(int source) {
static void PutSourcePropertyValue(nt::NetworkTable* table,
const cs::VideoEvent& event, bool isNew) {
wpi::SmallString<64> name;
wpi::SmallString<64> infoName;
if (wpi::StringRef{event.name}.startswith("raw_")) {
name = "RawProperty/";
name += event.name;
infoName = "RawPropertyInfo/";
infoName += event.name;
std::string_view namePrefix;
std::string_view infoPrefix;
if (wpi::starts_with(event.name, "raw_")) {
namePrefix = "RawProperty";
infoPrefix = "RawPropertyInfo";
} else {
name = "Property/";
name += event.name;
infoName = "PropertyInfo/";
infoName += event.name;
namePrefix = "Property";
infoPrefix = "PropertyInfo";
}
wpi::SmallString<64> buf;
CS_Status status = 0;
nt::NetworkTableEntry entry = table->GetEntry(name);
nt::NetworkTableEntry entry =
table->GetEntry(fmt::format("{}/{}", namePrefix, event.name));
switch (event.propertyKind) {
case CS_PROP_BOOLEAN:
if (isNew) {
@@ -277,13 +268,13 @@ static void PutSourcePropertyValue(nt::NetworkTable* table,
case CS_PROP_ENUM:
if (isNew) {
entry.SetDefaultDouble(event.value);
table->GetEntry(infoName + "/min")
table->GetEntry(fmt::format("{}/{}/min", infoPrefix, event.name))
.SetDouble(cs::GetPropertyMin(event.propertyHandle, &status));
table->GetEntry(infoName + "/max")
table->GetEntry(fmt::format("{}/{}/max", infoPrefix, event.name))
.SetDouble(cs::GetPropertyMax(event.propertyHandle, &status));
table->GetEntry(infoName + "/step")
table->GetEntry(fmt::format("{}/{}/step", infoPrefix, event.name))
.SetDouble(cs::GetPropertyStep(event.propertyHandle, &status));
table->GetEntry(infoName + "/default")
table->GetEntry(fmt::format("{}/{}/default", infoPrefix, event.name))
.SetDouble(cs::GetPropertyDefault(event.propertyHandle, &status));
} else {
entry.SetDouble(event.value);
@@ -301,7 +292,7 @@ static void PutSourcePropertyValue(nt::NetworkTable* table,
}
}
CameraServer::Impl::Impl() : m_nextPort(kBasePort) {
Instance::Instance() {
// We publish sources to NetworkTables using the following structure:
// "/CameraPublisher/{Source.Name}/" - root
// - "source" (string): Descriptive, prefixed with type (e.g. "usb:0")
@@ -404,12 +395,11 @@ CameraServer::Impl::Impl() : m_nextPort(kBasePort) {
case cs::VideoEvent::kSourcePropertyChoicesUpdated: {
auto table = GetSourceTable(event.sourceHandle);
if (table) {
wpi::SmallString<64> name{"PropertyInfo/"};
name += event.name;
name += "/choices";
auto choices =
cs::GetEnumPropertyChoices(event.propertyHandle, &status);
table->GetEntry(name).SetStringArray(choices);
table
->GetEntry(fmt::format("PropertyInfo/{}/choices", event.name))
.SetStringArray(choices);
}
break;
}
@@ -433,35 +423,35 @@ CameraServer::Impl::Impl() : m_nextPort(kBasePort) {
// else tries to change it.
wpi::SmallString<64> buf;
m_tableListener = nt::NetworkTableInstance::GetDefault().AddEntryListener(
kPublishName + wpi::Twine('/'),
fmt::format("{}/", kPublishName),
[=](const nt::EntryNotification& event) {
wpi::StringRef relativeKey =
event.name.substr(wpi::StringRef(kPublishName).size() + 1);
auto relativeKey = wpi::drop_front(
event.name, std::string_view{kPublishName}.size() + 1);
// get source (sourceName/...)
auto subKeyIndex = relativeKey.find('/');
if (subKeyIndex == wpi::StringRef::npos) {
if (subKeyIndex == std::string_view::npos) {
return;
}
wpi::StringRef sourceName = relativeKey.slice(0, subKeyIndex);
auto sourceName = wpi::slice(relativeKey, 0, subKeyIndex);
auto sourceIt = m_sources.find(sourceName);
if (sourceIt == m_sources.end()) {
return;
}
// get subkey
relativeKey = relativeKey.substr(subKeyIndex + 1);
relativeKey.remove_prefix(subKeyIndex + 1);
// handle standard names
wpi::StringRef propName;
std::string_view propName;
nt::NetworkTableEntry entry{event.entry};
if (relativeKey == "mode") {
// reset to current mode
entry.SetString(VideoModeToString(sourceIt->second.GetVideoMode()));
return;
} else if (relativeKey.startswith("Property/")) {
} else if (wpi::starts_with(relativeKey, "Property/")) {
propName = relativeKey.substr(9);
} else if (relativeKey.startswith("RawProperty/")) {
} else if (wpi::starts_with(relativeKey, "RawProperty/")) {
propName = relativeKey.substr(12);
} else {
return; // ignore
@@ -489,26 +479,23 @@ CameraServer::Impl::Impl() : m_nextPort(kBasePort) {
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_UPDATE);
}
CameraServer::CameraServer() : m_impl(new Impl) {}
CameraServer::~CameraServer() = default;
cs::UsbCamera CameraServer::StartAutomaticCapture() {
cs::UsbCamera camera = StartAutomaticCapture(m_impl->m_defaultUsbDevice++);
cs::UsbCamera camera =
StartAutomaticCapture(::GetInstance().m_defaultUsbDevice++);
auto csShared = GetCameraServerShared();
csShared->ReportUsbCamera(camera.GetHandle());
return camera;
}
cs::UsbCamera CameraServer::StartAutomaticCapture(int dev) {
cs::UsbCamera camera{"USB Camera " + wpi::Twine(dev), dev};
cs::UsbCamera camera{fmt::format("USB Camera {}", dev), dev};
StartAutomaticCapture(camera);
auto csShared = GetCameraServerShared();
csShared->ReportUsbCamera(camera.GetHandle());
return camera;
}
cs::UsbCamera CameraServer::StartAutomaticCapture(const wpi::Twine& name,
cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name,
int dev) {
cs::UsbCamera camera{name, dev};
StartAutomaticCapture(camera);
@@ -517,8 +504,8 @@ cs::UsbCamera CameraServer::StartAutomaticCapture(const wpi::Twine& name,
return camera;
}
cs::UsbCamera CameraServer::StartAutomaticCapture(const wpi::Twine& name,
const wpi::Twine& path) {
cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name,
std::string_view path) {
cs::UsbCamera camera{name, path};
StartAutomaticCapture(camera);
auto csShared = GetCameraServerShared();
@@ -526,7 +513,7 @@ cs::UsbCamera CameraServer::StartAutomaticCapture(const wpi::Twine& name,
return camera;
}
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& host) {
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view host) {
return AddAxisCamera("Axis Camera", host);
}
@@ -538,12 +525,12 @@ cs::AxisCamera CameraServer::AddAxisCamera(const std::string& host) {
return AddAxisCamera("Axis Camera", host);
}
cs::AxisCamera CameraServer::AddAxisCamera(wpi::ArrayRef<std::string> hosts) {
cs::AxisCamera CameraServer::AddAxisCamera(wpi::span<const std::string> hosts) {
return AddAxisCamera("Axis Camera", hosts);
}
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
const wpi::Twine& host) {
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
std::string_view host) {
cs::AxisCamera camera{name, host};
StartAutomaticCapture(camera);
auto csShared = GetCameraServerShared();
@@ -551,7 +538,7 @@ cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
return camera;
}
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
const char* host) {
cs::AxisCamera camera{name, host};
StartAutomaticCapture(camera);
@@ -560,7 +547,7 @@ cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
return camera;
}
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
const std::string& host) {
cs::AxisCamera camera{name, host};
StartAutomaticCapture(camera);
@@ -569,8 +556,8 @@ cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
return camera;
}
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
wpi::ArrayRef<std::string> hosts) {
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
wpi::span<const std::string> hosts) {
cs::AxisCamera camera{name, hosts};
StartAutomaticCapture(camera);
auto csShared = GetCameraServerShared();
@@ -578,11 +565,11 @@ cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
return camera;
}
cs::MjpegServer CameraServer::AddSwitchedCamera(const wpi::Twine& name) {
cs::MjpegServer CameraServer::AddSwitchedCamera(std::string_view name) {
// create a dummy CvSource
cs::CvSource source{name, cs::VideoMode::PixelFormat::kMJPEG, 160, 120, 30};
cs::MjpegServer server = StartAutomaticCapture(source);
m_impl->m_fixedSources[server.GetHandle()] = source.GetHandle();
::GetInstance().m_fixedSources[server.GetHandle()] = source.GetHandle();
return server;
}
@@ -590,22 +577,23 @@ cs::MjpegServer CameraServer::AddSwitchedCamera(const wpi::Twine& name) {
cs::MjpegServer CameraServer::StartAutomaticCapture(
const cs::VideoSource& camera) {
AddCamera(camera);
auto server = AddServer(wpi::Twine("serve_") + camera.GetName());
auto server = AddServer(fmt::format("serve_{}", camera.GetName()));
server.SetSource(camera);
return server;
}
cs::CvSink CameraServer::GetVideo() {
auto& inst = ::GetInstance();
cs::VideoSource source;
{
auto csShared = GetCameraServerShared();
std::scoped_lock lock(m_impl->m_mutex);
if (m_impl->m_primarySourceName.empty()) {
std::scoped_lock lock(inst.m_mutex);
if (inst.m_primarySourceName.empty()) {
csShared->SetCameraServerError("no camera available");
return cs::CvSink{};
}
auto it = m_impl->m_sources.find(m_impl->m_primarySourceName);
if (it == m_impl->m_sources.end()) {
auto it = inst.m_sources.find(inst.m_primarySourceName);
if (it == inst.m_sources.end()) {
csShared->SetCameraServerError("no camera available");
return cs::CvSink{};
}
@@ -615,40 +603,40 @@ cs::CvSink CameraServer::GetVideo() {
}
cs::CvSink CameraServer::GetVideo(const cs::VideoSource& camera) {
auto& inst = ::GetInstance();
wpi::SmallString<64> name{"opencv_"};
name += camera.GetName();
{
std::scoped_lock lock(m_impl->m_mutex);
auto it = m_impl->m_sinks.find(name);
if (it != m_impl->m_sinks.end()) {
std::scoped_lock lock(inst.m_mutex);
auto it = inst.m_sinks.find(name);
if (it != inst.m_sinks.end()) {
auto kind = it->second.GetKind();
if (kind != cs::VideoSink::kCv) {
auto csShared = GetCameraServerShared();
csShared->SetCameraServerError("expected OpenCV sink, but got " +
wpi::Twine(kind));
csShared->SetCameraServerError("expected OpenCV sink, but got {}",
kind);
return cs::CvSink{};
}
return *static_cast<cs::CvSink*>(&it->second);
}
}
cs::CvSink newsink{name};
cs::CvSink newsink{name.str()};
newsink.SetSource(camera);
AddServer(newsink);
return newsink;
}
cs::CvSink CameraServer::GetVideo(const wpi::Twine& name) {
wpi::SmallString<64> nameBuf;
wpi::StringRef nameStr = name.toStringRef(nameBuf);
cs::CvSink CameraServer::GetVideo(std::string_view name) {
auto& inst = ::GetInstance();
cs::VideoSource source;
{
std::scoped_lock lock(m_impl->m_mutex);
auto it = m_impl->m_sources.find(nameStr);
if (it == m_impl->m_sources.end()) {
std::scoped_lock lock(inst.m_mutex);
auto it = inst.m_sources.find(name);
if (it == inst.m_sources.end()) {
auto csShared = GetCameraServerShared();
csShared->SetCameraServerError("could not find camera " + nameStr);
csShared->SetCameraServerError("could not find camera {}", name);
return cs::CvSink{};
}
source = it->second;
@@ -656,89 +644,92 @@ cs::CvSink CameraServer::GetVideo(const wpi::Twine& name) {
return GetVideo(source);
}
cs::CvSource CameraServer::PutVideo(const wpi::Twine& name, int width,
cs::CvSource CameraServer::PutVideo(std::string_view name, int width,
int height) {
cs::CvSource source{name, cs::VideoMode::kMJPEG, width, height, 30};
StartAutomaticCapture(source);
return source;
}
cs::MjpegServer CameraServer::AddServer(const wpi::Twine& name) {
cs::MjpegServer CameraServer::AddServer(std::string_view name) {
auto& inst = ::GetInstance();
int port;
{
std::scoped_lock lock(m_impl->m_mutex);
port = m_impl->m_nextPort++;
std::scoped_lock lock(inst.m_mutex);
port = inst.m_nextPort++;
}
return AddServer(name, port);
}
cs::MjpegServer CameraServer::AddServer(const wpi::Twine& name, int port) {
cs::MjpegServer CameraServer::AddServer(std::string_view name, int port) {
cs::MjpegServer server{name, port};
AddServer(server);
return server;
}
void CameraServer::AddServer(const cs::VideoSink& server) {
std::scoped_lock lock(m_impl->m_mutex);
m_impl->m_sinks.try_emplace(server.GetName(), server);
auto& inst = ::GetInstance();
std::scoped_lock lock(inst.m_mutex);
inst.m_sinks.try_emplace(server.GetName(), server);
}
void CameraServer::RemoveServer(const wpi::Twine& name) {
std::scoped_lock lock(m_impl->m_mutex);
wpi::SmallString<64> nameBuf;
m_impl->m_sinks.erase(name.toStringRef(nameBuf));
void CameraServer::RemoveServer(std::string_view name) {
auto& inst = ::GetInstance();
std::scoped_lock lock(inst.m_mutex);
inst.m_sinks.erase(name);
}
cs::VideoSink CameraServer::GetServer() {
wpi::SmallString<64> name;
auto& inst = ::GetInstance();
std::string name;
{
std::scoped_lock lock(m_impl->m_mutex);
if (m_impl->m_primarySourceName.empty()) {
std::scoped_lock lock(inst.m_mutex);
if (inst.m_primarySourceName.empty()) {
auto csShared = GetCameraServerShared();
csShared->SetCameraServerError("no camera available");
return cs::VideoSink{};
}
name = "serve_";
name += m_impl->m_primarySourceName;
name = fmt::format("serve_{}", inst.m_primarySourceName);
}
return GetServer(name);
}
cs::VideoSink CameraServer::GetServer(const wpi::Twine& name) {
wpi::SmallString<64> nameBuf;
wpi::StringRef nameStr = name.toStringRef(nameBuf);
std::scoped_lock lock(m_impl->m_mutex);
auto it = m_impl->m_sinks.find(nameStr);
if (it == m_impl->m_sinks.end()) {
cs::VideoSink CameraServer::GetServer(std::string_view name) {
auto& inst = ::GetInstance();
std::scoped_lock lock(inst.m_mutex);
auto it = inst.m_sinks.find(name);
if (it == inst.m_sinks.end()) {
auto csShared = GetCameraServerShared();
csShared->SetCameraServerError("could not find server " + nameStr);
csShared->SetCameraServerError("could not find server {}", name);
return cs::VideoSink{};
}
return it->second;
}
void CameraServer::AddCamera(const cs::VideoSource& camera) {
auto& inst = ::GetInstance();
std::string name = camera.GetName();
std::scoped_lock lock(m_impl->m_mutex);
if (m_impl->m_primarySourceName.empty()) {
m_impl->m_primarySourceName = name;
std::scoped_lock lock(inst.m_mutex);
if (inst.m_primarySourceName.empty()) {
inst.m_primarySourceName = name;
}
m_impl->m_sources.try_emplace(name, camera);
inst.m_sources.try_emplace(name, camera);
}
void CameraServer::RemoveCamera(const wpi::Twine& name) {
std::scoped_lock lock(m_impl->m_mutex);
wpi::SmallString<64> nameBuf;
m_impl->m_sources.erase(name.toStringRef(nameBuf));
void CameraServer::RemoveCamera(std::string_view name) {
auto& inst = ::GetInstance();
std::scoped_lock lock(inst.m_mutex);
inst.m_sources.erase(name);
}
void CameraServer::SetSize(int size) {
std::scoped_lock lock(m_impl->m_mutex);
if (m_impl->m_primarySourceName.empty()) {
auto& inst = ::GetInstance();
std::scoped_lock lock(inst.m_mutex);
if (inst.m_primarySourceName.empty()) {
return;
}
auto it = m_impl->m_sources.find(m_impl->m_primarySourceName);
if (it == m_impl->m_sources.end()) {
auto it = inst.m_sources.find(inst.m_primarySourceName);
if (it == inst.m_sources.end()) {
return;
}
if (size == kSize160x120) {

View File

@@ -12,9 +12,12 @@ class DefaultCameraServerShared : public frc::CameraServerShared {
void ReportUsbCamera(int id) override {}
void ReportAxisCamera(int id) override {}
void ReportVideoServer(int id) override {}
void SetCameraServerError(const wpi::Twine& error) override {}
void SetVisionRunnerError(const wpi::Twine& error) override {}
void ReportDriverStationError(const wpi::Twine& error) override {}
void SetCameraServerErrorV(fmt::string_view format,
fmt::format_args args) override {}
void SetVisionRunnerErrorV(fmt::string_view format,
fmt::format_args args) override {}
void ReportDriverStationErrorV(fmt::string_view format,
fmt::format_args args) override {}
std::pair<std::thread::id, bool> GetRobotMainThreadId() const override {
return std::make_pair(std::thread::id(), false);
}

View File

@@ -33,7 +33,7 @@ void VisionRunnerBase::RunOnce() {
auto frameTime = m_cvSink.GrabFrame(*m_image);
if (frameTime == 0) {
auto error = m_cvSink.GetError();
csShared->ReportDriverStationError(error);
csShared->ReportDriverStationError(error.c_str());
} else {
DoProcess(*m_image);
}

View File

@@ -6,11 +6,11 @@
#include <stdint.h>
#include <memory>
#include <string>
#include <string_view>
#include <wpi/ArrayRef.h>
#include <wpi/Twine.h>
#include <wpi/deprecated.h>
#include <wpi/span.h>
#include "cscore.h"
#include "cscore_cv.h"
@@ -31,7 +31,9 @@ class CameraServer {
/**
* Get the CameraServer instance.
* @deprecated Use the static methods
*/
WPI_DEPRECATED("Use static methods")
static CameraServer* GetInstance();
/**
@@ -45,7 +47,7 @@ class CameraServer {
* with device 0, creating a camera named "USB Camera 0". Subsequent calls
* increment the device number (e.g. 1, 2, etc).
*/
cs::UsbCamera StartAutomaticCapture();
static cs::UsbCamera StartAutomaticCapture();
/**
* Start automatically capturing images to send to the dashboard.
@@ -55,7 +57,7 @@ class CameraServer {
*
* @param dev The device number of the camera interface
*/
cs::UsbCamera StartAutomaticCapture(int dev);
static cs::UsbCamera StartAutomaticCapture(int dev);
/**
* Start automatically capturing images to send to the dashboard.
@@ -63,7 +65,7 @@ class CameraServer {
* @param name The name to give the camera
* @param dev The device number of the camera interface
*/
cs::UsbCamera StartAutomaticCapture(const wpi::Twine& name, int dev);
static cs::UsbCamera StartAutomaticCapture(std::string_view name, int dev);
/**
* Start automatically capturing images to send to the dashboard.
@@ -71,8 +73,8 @@ class CameraServer {
* @param name The name to give the camera
* @param path The device path (e.g. "/dev/video0") of the camera
*/
cs::UsbCamera StartAutomaticCapture(const wpi::Twine& name,
const wpi::Twine& path);
static cs::UsbCamera StartAutomaticCapture(std::string_view name,
std::string_view path);
/**
* Start automatically capturing images to send to the dashboard from
@@ -80,7 +82,7 @@ class CameraServer {
*
* @param camera Camera
*/
cs::MjpegServer StartAutomaticCapture(const cs::VideoSource& camera);
static cs::MjpegServer StartAutomaticCapture(const cs::VideoSource& camera);
/**
* Adds an Axis IP camera.
@@ -89,7 +91,7 @@ class CameraServer {
*
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
*/
cs::AxisCamera AddAxisCamera(const wpi::Twine& host);
static cs::AxisCamera AddAxisCamera(std::string_view host);
/**
* Adds an Axis IP camera.
@@ -98,7 +100,7 @@ class CameraServer {
*
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
*/
cs::AxisCamera AddAxisCamera(const char* host);
static cs::AxisCamera AddAxisCamera(const char* host);
/**
* Adds an Axis IP camera.
@@ -107,7 +109,7 @@ class CameraServer {
*
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
*/
cs::AxisCamera AddAxisCamera(const std::string& host);
static cs::AxisCamera AddAxisCamera(const std::string& host);
/**
* Adds an Axis IP camera.
@@ -116,7 +118,7 @@ class CameraServer {
*
* @param hosts Array of Camera host IPs/DNS names
*/
cs::AxisCamera AddAxisCamera(wpi::ArrayRef<std::string> hosts);
static cs::AxisCamera AddAxisCamera(wpi::span<const std::string> hosts);
/**
* Adds an Axis IP camera.
@@ -126,7 +128,7 @@ class CameraServer {
* @param hosts Array of Camera host IPs/DNS names
*/
template <typename T>
cs::AxisCamera AddAxisCamera(std::initializer_list<T> hosts);
static cs::AxisCamera AddAxisCamera(std::initializer_list<T> hosts);
/**
* Adds an Axis IP camera.
@@ -134,7 +136,8 @@ class CameraServer {
* @param name The name to give the camera
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
*/
cs::AxisCamera AddAxisCamera(const wpi::Twine& name, const wpi::Twine& host);
static cs::AxisCamera AddAxisCamera(std::string_view name,
std::string_view host);
/**
* Adds an Axis IP camera.
@@ -142,7 +145,7 @@ class CameraServer {
* @param name The name to give the camera
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
*/
cs::AxisCamera AddAxisCamera(const wpi::Twine& name, const char* host);
static cs::AxisCamera AddAxisCamera(std::string_view name, const char* host);
/**
* Adds an Axis IP camera.
@@ -150,7 +153,8 @@ class CameraServer {
* @param name The name to give the camera
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
*/
cs::AxisCamera AddAxisCamera(const wpi::Twine& name, const std::string& host);
static cs::AxisCamera AddAxisCamera(std::string_view name,
const std::string& host);
/**
* Adds an Axis IP camera.
@@ -158,8 +162,8 @@ class CameraServer {
* @param name The name to give the camera
* @param hosts Array of Camera host IPs/DNS names
*/
cs::AxisCamera AddAxisCamera(const wpi::Twine& name,
wpi::ArrayRef<std::string> hosts);
static cs::AxisCamera AddAxisCamera(std::string_view name,
wpi::span<const std::string> hosts);
/**
* Adds an Axis IP camera.
@@ -168,8 +172,8 @@ class CameraServer {
* @param hosts Array of Camera host IPs/DNS names
*/
template <typename T>
cs::AxisCamera AddAxisCamera(const wpi::Twine& name,
std::initializer_list<T> hosts);
static cs::AxisCamera AddAxisCamera(std::string_view name,
std::initializer_list<T> hosts);
/**
* Adds a virtual camera for switching between two streams. Unlike the
@@ -177,7 +181,7 @@ class CameraServer {
* VideoSource. Calling SetSource() on the returned object can be used
* to switch the actual source of the stream.
*/
cs::MjpegServer AddSwitchedCamera(const wpi::Twine& name);
static cs::MjpegServer AddSwitchedCamera(std::string_view name);
/**
* Get OpenCV access to the primary camera feed. This allows you to
@@ -186,7 +190,7 @@ class CameraServer {
* <p>This is only valid to call after a camera feed has been added
* with startAutomaticCapture() or addServer().
*/
cs::CvSink GetVideo();
static cs::CvSink GetVideo();
/**
* Get OpenCV access to the specified camera. This allows you to get
@@ -194,7 +198,7 @@ class CameraServer {
*
* @param camera Camera (e.g. as returned by startAutomaticCapture).
*/
cs::CvSink GetVideo(const cs::VideoSource& camera);
static cs::CvSink GetVideo(const cs::VideoSource& camera);
/**
* Get OpenCV access to the specified camera. This allows you to get
@@ -202,7 +206,7 @@ class CameraServer {
*
* @param name Camera name
*/
cs::CvSink GetVideo(const wpi::Twine& name);
static cs::CvSink GetVideo(std::string_view name);
/**
* Create a MJPEG stream with OpenCV input. This can be called to pass custom
@@ -212,35 +216,35 @@ class CameraServer {
* @param width Width of the image being sent
* @param height Height of the image being sent
*/
cs::CvSource PutVideo(const wpi::Twine& name, int width, int height);
static cs::CvSource PutVideo(std::string_view name, int width, int height);
/**
* Adds a MJPEG server at the next available port.
*
* @param name Server name
*/
cs::MjpegServer AddServer(const wpi::Twine& name);
static cs::MjpegServer AddServer(std::string_view name);
/**
* Adds a MJPEG server.
*
* @param name Server name
*/
cs::MjpegServer AddServer(const wpi::Twine& name, int port);
static cs::MjpegServer AddServer(std::string_view name, int port);
/**
* Adds an already created server.
*
* @param server Server
*/
void AddServer(const cs::VideoSink& server);
static void AddServer(const cs::VideoSink& server);
/**
* Removes a server by name.
*
* @param name Server name
*/
void RemoveServer(const wpi::Twine& name);
static void RemoveServer(std::string_view name);
/**
* Get server for the primary camera feed.
@@ -248,28 +252,28 @@ class CameraServer {
* This is only valid to call after a camera feed has been added with
* StartAutomaticCapture() or AddServer().
*/
cs::VideoSink GetServer();
static cs::VideoSink GetServer();
/**
* Gets a server by name.
*
* @param name Server name
*/
cs::VideoSink GetServer(const wpi::Twine& name);
static cs::VideoSink GetServer(std::string_view name);
/**
* Adds an already created camera.
*
* @param camera Camera
*/
void AddCamera(const cs::VideoSource& camera);
static void AddCamera(const cs::VideoSource& camera);
/**
* Removes a camera by name.
*
* @param name Camera name
*/
void RemoveCamera(const wpi::Twine& name);
static void RemoveCamera(std::string_view name);
/**
* Sets the size of the image to use. Use the public kSize constants to set
@@ -280,14 +284,10 @@ class CameraServer {
* StartAutomaticCapture() instead.
* @param size The size to use
*/
void SetSize(int size);
static void SetSize(int size);
private:
CameraServer();
~CameraServer();
struct Impl;
std::unique_ptr<Impl> m_impl;
CameraServer() = default;
};
} // namespace frc

View File

@@ -19,7 +19,7 @@ inline cs::AxisCamera CameraServer::AddAxisCamera(
template <typename T>
inline cs::AxisCamera CameraServer::AddAxisCamera(
const wpi::Twine& name, std::initializer_list<T> hosts) {
std::string_view name, std::initializer_list<T> hosts) {
std::vector<std::string> vec;
vec.reserve(hosts.size());
for (const auto& host : hosts) {

View File

@@ -8,7 +8,7 @@
#include <thread>
#include <utility>
#include <wpi/Twine.h>
#include <fmt/format.h>
namespace frc {
class CameraServerShared {
@@ -17,10 +17,31 @@ class CameraServerShared {
virtual void ReportUsbCamera(int id) = 0;
virtual void ReportAxisCamera(int id) = 0;
virtual void ReportVideoServer(int id) = 0;
virtual void SetCameraServerError(const wpi::Twine& error) = 0;
virtual void SetVisionRunnerError(const wpi::Twine& error) = 0;
virtual void ReportDriverStationError(const wpi::Twine& error) = 0;
virtual void SetCameraServerErrorV(fmt::string_view format,
fmt::format_args args) = 0;
virtual void SetVisionRunnerErrorV(fmt::string_view format,
fmt::format_args args) = 0;
virtual void ReportDriverStationErrorV(fmt::string_view format,
fmt::format_args args) = 0;
virtual std::pair<std::thread::id, bool> GetRobotMainThreadId() const = 0;
template <typename S, typename... Args>
inline void SetCameraServerError(const S& format, Args&&... args) {
SetCameraServerErrorV(format,
fmt::make_args_checked<Args...>(format, args...));
}
template <typename S, typename... Args>
inline void SetVisionRunnerError(const S& format, Args&&... args) {
SetVisionRunnerErrorV(format,
fmt::make_args_checked<Args...>(format, args...));
}
template <typename S, typename... Args>
inline void ReportDriverStationError(const S& format, Args&&... args) {
ReportDriverStationErrorV(format,
fmt::make_args_checked<Args...>(format, args...));
}
};
CameraServerShared* GetCameraServerShared();

View File

@@ -49,9 +49,9 @@ class VisionRunnerBase {
void RunOnce();
/**
* A convenience method that calls {@link #runOnce()} in an infinite loop.
* This must be run in a dedicated thread, and cannot be used in the main
* robot thread because it will freeze the robot program.
* A convenience method that calls runOnce() in an infinite loop. This must be
* run in a dedicated thread, and cannot be used in the main robot thread
* because it will freeze the robot program.
*
* <strong>Do not call this method directly from the main thread.</strong>
*/

View File

@@ -7,7 +7,7 @@ GET_FILENAME_COMPONENT(inputBase ${input} NAME)
STRING(REGEX REPLACE "[^a-zA-Z0-9]" "_" funcName "${inputBase}")
SET(funcName "GetResource_${funcName}")
FILE(WRITE "${output}" "#include <stddef.h>\n#include <wpi/StringRef.h>\nextern \"C\" {\nstatic const unsigned char contents[] = {")
FILE(WRITE "${output}" "#include <stddef.h>\n#include <string_view>\nextern \"C\" {\nstatic const unsigned char contents[] = {")
STRING(REGEX MATCHALL ".." outputData "${fileHex}")
STRING(REGEX REPLACE ";" ", 0x" outputData "${outputData}")
@@ -17,7 +17,7 @@ FILE(APPEND "${output}" "const unsigned char* ${prefix}${funcName}(size_t* len)
IF(NOT namespace STREQUAL "")
FILE(APPEND "${output}" "namespace ${namespace} {\n")
ENDIF()
FILE(APPEND "${output}" "wpi::StringRef ${funcName}() {\n return wpi::StringRef(reinterpret_cast<const char*>(contents), ${fileSize});\n}\n")
FILE(APPEND "${output}" "std::string_view ${funcName}() {\n return std::string_view(reinterpret_cast<const char*>(contents), ${fileSize});\n}\n")
IF(NOT namespace STREQUAL "")
FILE(APPEND "${output}" "}\n")
ENDIF()

View File

@@ -0,0 +1,118 @@
import org.gradle.language.base.internal.ProjectLayout
import jaci.gradle.toolchains.*
import jaci.gradle.nativedeps.*
import org.gradle.internal.os.OperatingSystem
apply plugin: 'cpp'
apply plugin: 'visual-studio'
apply plugin: 'edu.wpi.first.NativeUtils'
apply plugin: ExtraTasks
apply plugin: 'edu.wpi.first.NativeUtils'
apply plugin: 'jaci.gradle.EmbeddedTools'
apply from: '../shared/config.gradle'
ext {
sharedCvConfigs = [crossConnIntegrationTests: []]
staticCvConfigs = [:]
useJava = false
useCpp = true
staticGtestConfigs = [crossConnIntegrationTests: []]
}
apply from: "${rootDir}/shared/opencv.gradle"
apply from: "${rootDir}/shared/googletest.gradle"
model {
components {
crossConnIntegrationTests(NativeExecutableSpec) {
targetBuildTypes 'debug'
nativeUtils.useRequiredLibrary(it, 'googletest_static')
binaries.all { binary ->
if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
binary.sources {
athenaCpp(CppSourceSet) {
source {
srcDirs = ['src/main/native/cpp']
includes = ['**/*.cpp']
}
exportedHeaders {
srcDirs = ['src/main/native/include']
includes = ['**/*.h']
}
}
}
binary.tasks.withType(CppCompile) {
cppCompiler.args "-Wno-missing-field-initializers"
cppCompiler.args "-Wno-unused-variable"
cppCompiler.args "-Wno-error=deprecated-declarations"
}
project(':hal').addHalDependency(binary, 'shared')
project(':hal').addHalJniDependency(binary)
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
nativeUtils.useRequiredLibrary(binary, 'netcomm_shared', 'chipobject_shared', 'visa_shared', 'ni_runtime_shared')
}
} else {
binary.sources {
simCpp(CppSourceSet) {
source {
srcDirs 'src/main/native/dt'
includes = ['**/*.cpp']
}
}
}
}
}
}
}
}
deploy {
targets {
target('roborio') {
directory = '/home/admin'
maxChannels = 4
locations {
ssh {
address = "172.22.11.2"
user = 'admin'
password = ''
ipv6 = false
}
}
}
}
artifacts {
all {
targets << 'roborio'
predeploy << { ctx ->
ctx.execute('/usr/local/frc/bin/frcKillRobot.sh -t')
}
postdeploy << { ctx ->
ctx.execute("sync")
ctx.execute("ldconfig")
}
}
nativeArtifact('crossConnIntegrationTests') {
component = 'crossConnIntegrationTests'
targetPlatform = nativeUtils.wpi.platforms.roborio
libraryDirectory = '/usr/local/frc/third-party/lib'
buildType = 'debug'
postdeploy << { ctx ->
ctx.execute('chmod +x crossConnIntegrationTests')
}
}
}
}
tasks.register('deployTests') {
try {
dependsOn tasks.named('deployCrossConnIntegrationTestsLibrariesRoborio')
dependsOn tasks.named('deployCrossConnIntegrationTestsRoborio')
} catch (ignored) {
}
}

View File

@@ -0,0 +1,112 @@
// 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 <hal/AnalogInput.h>
#include <hal/AnalogOutput.h>
#include <wpi/SmallVector.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
#include "gtest/gtest.h"
using namespace hlt;
class AnalogCrossTest : public ::testing::TestWithParam<std::pair<int, int>> {};
TEST_P(AnalogCrossTest, TestAnalogCross) {
auto param = GetParam();
int32_t status = 0;
AnalogInputHandle input{param.first, &status};
ASSERT_EQ(0, status);
AnalogOutputHandle output{param.second, &status};
ASSERT_EQ(0, status);
for (double i = 0; i < 5; i += 0.1) {
HAL_SetAnalogOutput(output, i, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_NEAR(i, HAL_GetAnalogVoltage(input, &status), 0.01);
ASSERT_EQ(0, status);
}
for (double i = 5; i > 0; i -= 0.1) {
HAL_SetAnalogOutput(output, i, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_NEAR(i, HAL_GetAnalogVoltage(input, &status), 0.01);
ASSERT_EQ(0, status);
}
}
TEST(AnalogInputTest, TestAllocateAll) {
wpi::SmallVector<AnalogInputHandle, 21> analogHandles;
for (int i = 0; i < HAL_GetNumAnalogInputs(); i++) {
int32_t status = 0;
analogHandles.emplace_back(AnalogInputHandle(i, &status));
ASSERT_EQ(status, 0);
}
}
TEST(AnalogInputTest, TestMultipleAllocateFails) {
int32_t status = 0;
AnalogInputHandle handle(0, &status);
ASSERT_NE(handle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
AnalogInputHandle handle2(0, &status);
ASSERT_EQ(handle2, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
TEST(AnalogInputTest, TestOverAllocateFails) {
int32_t status = 0;
AnalogInputHandle handle(HAL_GetNumAnalogInputs(), &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(AnalogInputTest, TestUnderAllocateFails) {
int32_t status = 0;
AnalogInputHandle handle(-1, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(AnalogOutputTest, TestAllocateAll) {
wpi::SmallVector<AnalogOutputHandle, 21> analogHandles;
for (int i = 0; i < HAL_GetNumAnalogOutputs(); i++) {
int32_t status = 0;
analogHandles.emplace_back(AnalogOutputHandle(i, &status));
ASSERT_EQ(status, 0);
}
}
TEST(AnalogOutputTest, TestMultipleAllocateFails) {
int32_t status = 0;
AnalogOutputHandle handle(0, &status);
ASSERT_NE(handle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
AnalogOutputHandle handle2(0, &status);
ASSERT_EQ(handle2, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
TEST(AnalogOutputTest, TestOverAllocateFails) {
int32_t status = 0;
AnalogOutputHandle handle(HAL_GetNumAnalogOutputs(), &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(AnalogOutputTest, TestUnderAllocateFails) {
int32_t status = 0;
AnalogOutputHandle handle(-1, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
INSTANTIATE_TEST_SUITE_P(AnalogCrossConnectsTest, AnalogCrossTest,
::testing::ValuesIn(AnalogCrossConnects));

View File

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

View File

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

View File

@@ -5,7 +5,6 @@
#include "gtest/gtest.h"
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
return ret;
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

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

View File

@@ -0,0 +1,50 @@
// 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 <hal/AnalogInput.h>
#include <hal/Relay.h>
#include <wpi/SmallVector.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
#include "gtest/gtest.h"
using namespace hlt;
class RelayAnalogTest : public ::testing::TestWithParam<std::pair<int, int>> {};
TEST_P(RelayAnalogTest, TestRelayAnalogCross) {
auto param = GetParam();
int32_t status = 0;
RelayHandle relay{param.first, true, &status};
ASSERT_EQ(0, status);
AnalogInputHandle analog{param.second, &status};
ASSERT_EQ(0, status);
AnalogTriggerHandle trigger{analog, &status};
ASSERT_EQ(0, status);
HAL_SetAnalogTriggerLimitsVoltage(trigger, 1.5, 3.0, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(relay, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetAnalogTriggerTriggerState(trigger, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(relay, true, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_TRUE(HAL_GetAnalogTriggerTriggerState(trigger, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(relay, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetAnalogTriggerTriggerState(trigger, &status));
ASSERT_EQ(0, status);
}
INSTANTIATE_TEST_SUITE_P(RelayAnalogCrossConnectsTest, RelayAnalogTest,
::testing::ValuesIn(RelayAnalogCrossConnects));

View File

@@ -0,0 +1,104 @@
// 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 <hal/Relay.h>
#include <wpi/SmallVector.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
#include "gtest/gtest.h"
using namespace hlt;
class RelayDigitalTest : public ::testing::TestWithParam<RelayCross> {};
TEST_P(RelayDigitalTest, TestRelayCross) {
auto param = GetParam();
int32_t status = 0;
RelayHandle fwd{param.Relay, true, &status};
ASSERT_EQ(0, status);
RelayHandle rev{param.Relay, false, &status};
ASSERT_EQ(0, status);
DIOHandle fwdInput{param.FwdDio, true, &status};
ASSERT_EQ(0, status);
DIOHandle revInput{param.RevDio, true, &status};
ASSERT_EQ(0, status);
HAL_SetRelay(fwd, false, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(rev, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetDIO(fwdInput, &status));
ASSERT_EQ(0, status);
ASSERT_FALSE(HAL_GetDIO(revInput, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(fwd, false, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(rev, true, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetDIO(fwdInput, &status));
ASSERT_EQ(0, status);
ASSERT_TRUE(HAL_GetDIO(revInput, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(fwd, true, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(rev, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_TRUE(HAL_GetDIO(fwdInput, &status));
ASSERT_EQ(0, status);
ASSERT_FALSE(HAL_GetDIO(revInput, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(fwd, true, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(rev, true, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_TRUE(HAL_GetDIO(fwdInput, &status));
ASSERT_EQ(0, status);
ASSERT_TRUE(HAL_GetDIO(revInput, &status));
ASSERT_EQ(0, status);
}
TEST(RelayDigitalTest, TestAllocateAll) {
wpi::SmallVector<RelayHandle, 32> relayHandles;
for (int i = 0; i < HAL_GetNumRelayChannels(); i++) {
int32_t status = 0;
relayHandles.emplace_back(i / 2, i % 2, &status);
ASSERT_EQ(status, 0);
}
}
TEST(RelayDigitalTest, TestMultipleAllocateFails) {
int32_t status = 0;
RelayHandle handle(0, true, &status);
ASSERT_NE(handle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
RelayHandle handle2(0, true, &status);
ASSERT_EQ(handle2, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
TEST(RelayDigitalTest, TestOverAllocateFails) {
int32_t status = 0;
RelayHandle handle(HAL_GetNumRelayChannels(), true, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(RelayDigitalTest, TestUnderAllocateFails) {
int32_t status = 0;
RelayHandle handle(-1, true, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
INSTANTIATE_TEST_SUITE_P(RelayDigitalCrossConnectsTest, RelayDigitalTest,
::testing::ValuesIn(RelayCrossConnects));

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,62 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <array>
#include <utility>
namespace hlt {
constexpr static std::array<std::pair<int, int>, 22> DIOCrossConnects{
std::pair{20, 25},
std::pair{19, 24},
std::pair{17, 13},
std::pair{16, 12},
std::pair{15, 11},
std::pair{14, 10},
std::pair{26, 2},
std::pair{27, 1},
std::pair{28, 0},
std::pair{29, 3},
std::pair{30, 4},
// Opposite direction
std::pair{25, 20},
std::pair{24, 19},
std::pair{13, 17},
std::pair{12, 16},
std::pair{11, 15},
std::pair{10, 14},
std::pair{2, 26},
std::pair{1, 27},
std::pair{0, 28},
std::pair{3, 29},
std::pair{4, 30},
};
// PWM on left, DIO on right
constexpr static std::array<std::pair<int, int>, 2> PWMCrossConnects{
std::pair{0, 18},
std::pair{16, 25},
};
// FWD only, relay on left
constexpr static std::array<std::pair<int, int>, 2> RelayAnalogCrossConnects{
std::pair{2, 0}, std::pair{3, 1}};
struct RelayCross {
int Relay;
int FwdDio;
int RevDio;
};
constexpr static std::array<RelayCross, 1> RelayCrossConnects{
RelayCross{0, 23, 22}};
// input on left
constexpr static std::array<std::pair<int, int>, 2> AnalogCrossConnects{
std::pair{2, 0}, std::pair{4, 1}};
} // namespace hlt

View File

@@ -0,0 +1,200 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <hal/DMA.h>
#include <hal/DutyCycle.h>
#include <hal/HAL.h>
namespace hlt {
struct InterruptHandle {
public:
explicit InterruptHandle(int32_t* status) {
handle = HAL_InitializeInterrupts(status);
}
InterruptHandle(const InterruptHandle&) = delete;
InterruptHandle operator=(const InterruptHandle&) = delete;
InterruptHandle(InterruptHandle&&) = default;
InterruptHandle& operator=(InterruptHandle&&) = default;
~InterruptHandle() { HAL_CleanInterrupts(handle); }
operator HAL_InterruptHandle() const { return handle; }
private:
HAL_InterruptHandle handle = 0;
};
struct DMAHandle {
public:
explicit DMAHandle(int32_t* status) { handle = HAL_InitializeDMA(status); }
DMAHandle(const DMAHandle&) = delete;
DMAHandle operator=(const DMAHandle&) = delete;
DMAHandle(DMAHandle&&) = default;
DMAHandle& operator=(DMAHandle&&) = default;
~DMAHandle() { HAL_FreeDMA(handle); }
operator HAL_DMAHandle() const { return handle; }
private:
HAL_DMAHandle handle = 0;
};
struct AnalogInputHandle {
public:
AnalogInputHandle(int32_t port, int32_t* status) {
handle = HAL_InitializeAnalogInputPort(HAL_GetPort(port), nullptr, status);
}
AnalogInputHandle(const AnalogInputHandle&) = delete;
AnalogInputHandle operator=(const AnalogInputHandle&) = delete;
AnalogInputHandle(AnalogInputHandle&&) = default;
AnalogInputHandle& operator=(AnalogInputHandle&&) = default;
~AnalogInputHandle() { HAL_FreeAnalogInputPort(handle); }
operator HAL_AnalogInputHandle() const { return handle; }
private:
HAL_AnalogInputHandle handle = 0;
};
struct AnalogOutputHandle {
public:
AnalogOutputHandle(int32_t port, int32_t* status) {
handle = HAL_InitializeAnalogOutputPort(HAL_GetPort(port), nullptr, status);
}
AnalogOutputHandle(const AnalogOutputHandle&) = delete;
AnalogOutputHandle operator=(const AnalogOutputHandle&) = delete;
AnalogOutputHandle(AnalogOutputHandle&&) = default;
AnalogOutputHandle& operator=(AnalogOutputHandle&&) = default;
~AnalogOutputHandle() { HAL_FreeAnalogOutputPort(handle); }
operator HAL_AnalogOutputHandle() const { return handle; }
private:
HAL_AnalogOutputHandle handle = 0;
};
struct AnalogTriggerHandle {
public:
explicit AnalogTriggerHandle(HAL_AnalogInputHandle port, int32_t* status) {
handle = HAL_InitializeAnalogTrigger(port, status);
}
AnalogTriggerHandle(const AnalogTriggerHandle&) = delete;
AnalogTriggerHandle operator=(const AnalogTriggerHandle&) = delete;
AnalogTriggerHandle(AnalogTriggerHandle&&) = default;
AnalogTriggerHandle& operator=(AnalogTriggerHandle&&) = default;
~AnalogTriggerHandle() {
int32_t status = 0;
HAL_CleanAnalogTrigger(handle, &status);
}
operator HAL_AnalogTriggerHandle() const { return handle; }
private:
HAL_AnalogTriggerHandle handle = 0;
};
struct DutyCycleHandle {
public:
DutyCycleHandle(HAL_DigitalHandle port, int32_t* status) {
handle = HAL_InitializeDutyCycle(
port, HAL_AnalogTriggerType::HAL_Trigger_kInWindow, status);
}
DutyCycleHandle(const DutyCycleHandle&) = delete;
DutyCycleHandle operator=(const DutyCycleHandle&) = delete;
DutyCycleHandle(DutyCycleHandle&&) = default;
DutyCycleHandle& operator=(DutyCycleHandle&&) = default;
~DutyCycleHandle() { HAL_FreeDutyCycle(handle); }
operator HAL_DutyCycleHandle() const { return handle; }
private:
HAL_DutyCycleHandle handle = 0;
};
struct DIOHandle {
public:
DIOHandle() {}
DIOHandle(const DIOHandle&) = delete;
DIOHandle operator=(const DIOHandle&) = delete;
DIOHandle(DIOHandle&&) = default;
DIOHandle& operator=(DIOHandle&&) = default;
DIOHandle(int32_t port, HAL_Bool input, int32_t* status) {
handle = HAL_InitializeDIOPort(HAL_GetPort(port), input, nullptr, status);
}
~DIOHandle() { HAL_FreeDIOPort(handle); }
operator HAL_DigitalHandle() const { return handle; }
private:
HAL_DigitalHandle handle = 0;
};
struct PWMHandle {
public:
PWMHandle() {}
PWMHandle(const PWMHandle&) = delete;
PWMHandle operator=(const PWMHandle&) = delete;
PWMHandle(PWMHandle&&) = default;
PWMHandle& operator=(PWMHandle&&) = default;
PWMHandle(int32_t port, int32_t* status) {
handle = HAL_InitializePWMPort(HAL_GetPort(port), nullptr, status);
}
~PWMHandle() {
int32_t status = 0;
HAL_FreePWMPort(handle, &status);
}
operator HAL_DigitalHandle() const { return handle; }
private:
HAL_DigitalHandle handle = 0;
};
struct RelayHandle {
public:
RelayHandle(int32_t port, HAL_Bool fwd, int32_t* status) {
handle = HAL_InitializeRelayPort(HAL_GetPort(port), fwd, nullptr, status);
}
RelayHandle(const RelayHandle&) = delete;
RelayHandle operator=(const RelayHandle&) = delete;
RelayHandle(RelayHandle&&) = default;
RelayHandle& operator=(RelayHandle&&) = default;
~RelayHandle() { HAL_FreeRelayPort(handle); }
operator HAL_RelayHandle() const { return handle; }
private:
HAL_RelayHandle handle = 0;
};
#define ASSERT_LAST_ERROR_STATUS(status, x) \
do { \
ASSERT_EQ(status, HAL_USE_LAST_ERROR); \
const char* lastErrorMessageInMacro = HAL_GetLastError(&status); \
ASSERT_EQ(status, x); \
} while (0)
} // namespace hlt

View File

@@ -31,6 +31,7 @@ repoRootNameOverride {
}
includeOtherLibs {
^fmt/
^opencv2/
^support/
^tcpsockets/

View File

@@ -2,83 +2,77 @@
// 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/SmallString.h>
#include <wpi/raw_ostream.h>
#include <cstdio>
#include <fmt/format.h>
#include "cscore.h"
int main() {
CS_Status status = 0;
wpi::SmallString<64> buf;
for (const auto& caminfo : cs::EnumerateUsbCameras(&status)) {
wpi::outs() << caminfo.dev << ": " << caminfo.path << " (" << caminfo.name
<< ")\n";
fmt::print("{}: {} ({})\n", caminfo.dev, caminfo.path, caminfo.name);
if (!caminfo.otherPaths.empty()) {
wpi::outs() << "Other device paths:\n";
std::puts("Other device paths:");
for (auto&& path : caminfo.otherPaths) {
wpi::outs() << " " << path << '\n';
fmt::print(" {}\n", path);
}
}
cs::UsbCamera camera{"usbcam", caminfo.dev};
wpi::outs() << "Properties:\n";
std::puts("Properties:");
for (const auto& prop : camera.EnumerateProperties()) {
wpi::outs() << " " << prop.GetName();
fmt::print(" {}", prop.GetName());
switch (prop.GetKind()) {
case cs::VideoProperty::kBoolean:
wpi::outs() << " (bool): "
<< "value=" << prop.Get()
<< " default=" << prop.GetDefault();
fmt::print(" (bool): value={} default={}", prop.Get(),
prop.GetDefault());
break;
case cs::VideoProperty::kInteger:
wpi::outs() << " (int): "
<< "value=" << prop.Get() << " min=" << prop.GetMin()
<< " max=" << prop.GetMax() << " step=" << prop.GetStep()
<< " default=" << prop.GetDefault();
fmt::print(" (int): value={} min={} max={} step={} default={}",
prop.Get(), prop.GetMin(), prop.GetMax(), prop.GetStep(),
prop.GetDefault());
break;
case cs::VideoProperty::kString:
wpi::outs() << " (string): " << prop.GetString(buf);
fmt::print(" (string): {}", prop.GetString());
break;
case cs::VideoProperty::kEnum: {
wpi::outs() << " (enum): "
<< "value=" << prop.Get();
fmt::print(" (enum): value={}", prop.Get());
auto choices = prop.GetChoices();
for (size_t i = 0; i < choices.size(); ++i) {
if (choices[i].empty()) {
continue;
if (!choices[i].empty()) {
fmt::print("\n {}: {}", i, choices[i]);
}
wpi::outs() << "\n " << i << ": " << choices[i];
}
break;
}
default:
break;
}
wpi::outs() << '\n';
std::fputc('\n', stdout);
}
wpi::outs() << "Video Modes:\n";
std::puts("Video Modes:");
for (const auto& mode : camera.EnumerateVideoModes()) {
wpi::outs() << " PixelFormat:";
const char* pixelFormat;
switch (mode.pixelFormat) {
case cs::VideoMode::kMJPEG:
wpi::outs() << "MJPEG";
pixelFormat = "MJPEG";
break;
case cs::VideoMode::kYUYV:
wpi::outs() << "YUYV";
pixelFormat = "YUYV";
break;
case cs::VideoMode::kRGB565:
wpi::outs() << "RGB565";
pixelFormat = "RGB565";
break;
default:
wpi::outs() << "Unknown";
pixelFormat = "Unknown";
break;
}
wpi::outs() << " Width:" << mode.width;
wpi::outs() << " Height:" << mode.height;
wpi::outs() << " FPS:" << mode.fps << '\n';
fmt::print(" PixelFormat:{} Width:{} Height:{} FPS:{}\n", pixelFormat,
mode.width, mode.height, mode.fps);
}
}
}

View File

@@ -3,23 +3,27 @@
// the WPILib BSD license file in the root directory of this project.
#include <chrono>
#include <cstdio>
#include <thread>
#include <wpi/SmallString.h>
#include <wpi/raw_ostream.h>
#include <fmt/format.h>
#include <wpi/StringExtras.h>
#include "cscore.h"
int main(int argc, char** argv) {
if (argc < 2) {
wpi::errs() << "Usage: settings camera [prop val] ... -- [prop val]...\n";
wpi::errs() << " Example: settings 1 brightness 30 raw_contrast 10\n";
std::fputs("Usage: settings camera [prop val] ... -- [prop val]...\n",
stderr);
std::fputs(" Example: settings 1 brightness 30 raw_contrast 10\n", stderr);
return 1;
}
int id;
if (wpi::StringRef{argv[1]}.getAsInteger(10, id)) {
wpi::errs() << "Expected number for camera\n";
if (auto v = wpi::parse_integer<int>(argv[1], 10)) {
id = v.value();
} else {
std::fputs("Expected number for camera\n", stderr);
return 2;
}
@@ -27,22 +31,21 @@ int main(int argc, char** argv) {
// Set prior to connect
int arg = 2;
wpi::StringRef propName;
for (; arg < argc && wpi::StringRef{argv[arg]} != "--"; ++arg) {
std::string_view propName;
for (; arg < argc && std::string_view{argv[arg]} != "--"; ++arg) {
if (propName.empty()) {
propName = argv[arg];
} else {
wpi::StringRef propVal{argv[arg]};
int intVal;
if (propVal.getAsInteger(10, intVal)) {
camera.GetProperty(propName).SetString(propVal);
std::string_view propVal{argv[arg]};
if (auto v = wpi::parse_integer<int>(propVal, 10)) {
camera.GetProperty(propName).Set(v.value());
} else {
camera.GetProperty(propName).Set(intVal);
camera.GetProperty(propName).SetString(propVal);
}
propName = wpi::StringRef{};
propName = {};
}
}
if (arg < argc && wpi::StringRef{argv[arg]} == "--") {
if (arg < argc && std::string_view{argv[arg]} == "--") {
++arg;
}
@@ -52,57 +55,51 @@ int main(int argc, char** argv) {
}
// Set rest
propName = wpi::StringRef{};
propName = {};
for (; arg < argc; ++arg) {
if (propName.empty()) {
propName = argv[arg];
} else {
wpi::StringRef propVal{argv[arg]};
int intVal;
if (propVal.getAsInteger(10, intVal)) {
camera.GetProperty(propName).SetString(propVal);
std::string_view propVal{argv[arg]};
if (auto v = wpi::parse_integer<int>(propVal, 10)) {
camera.GetProperty(propName).Set(v.value());
} else {
camera.GetProperty(propName).Set(intVal);
camera.GetProperty(propName).SetString(propVal);
}
propName = wpi::StringRef{};
propName = {};
}
}
// Print settings
wpi::SmallString<64> buf;
wpi::outs() << "Properties:\n";
std::puts("Properties:");
for (const auto& prop : camera.EnumerateProperties()) {
wpi::outs() << " " << prop.GetName();
fmt::print(" {}", prop.GetName());
switch (prop.GetKind()) {
case cs::VideoProperty::kBoolean:
wpi::outs() << " (bool): "
<< "value=" << prop.Get()
<< " default=" << prop.GetDefault();
fmt::print(" (bool): value={} default={}", prop.Get(),
prop.GetDefault());
break;
case cs::VideoProperty::kInteger:
wpi::outs() << " (int): "
<< "value=" << prop.Get() << " min=" << prop.GetMin()
<< " max=" << prop.GetMax() << " step=" << prop.GetStep()
<< " default=" << prop.GetDefault();
fmt::print(" (int): value={} min={} max={} step={} default={}",
prop.Get(), prop.GetMin(), prop.GetMax(), prop.GetStep(),
prop.GetDefault());
break;
case cs::VideoProperty::kString:
wpi::outs() << " (string): " << prop.GetString(buf);
fmt::print(" (string): {}", prop.GetString());
break;
case cs::VideoProperty::kEnum: {
wpi::outs() << " (enum): "
<< "value=" << prop.Get();
fmt::print(" (enum): value={}", prop.Get());
auto choices = prop.GetChoices();
for (size_t i = 0; i < choices.size(); ++i) {
if (choices[i].empty()) {
continue;
if (!choices[i].empty()) {
fmt::print("\n {}: {}", i, choices[i]);
}
wpi::outs() << "\n " << i << ": " << choices[i];
}
break;
}
default:
break;
}
wpi::outs() << '\n';
std::fputc('\n', stdout);
}
}

View File

@@ -4,15 +4,15 @@
#include <cstdio>
#include <wpi/raw_ostream.h>
#include <fmt/format.h>
#include "cscore.h"
int main() {
wpi::outs() << "hostname: " << cs::GetHostname() << '\n';
wpi::outs() << "IPv4 network addresses:\n";
fmt::print("hostname: {}\n", cs::GetHostname());
std::puts("IPv4 network addresses:");
for (const auto& addr : cs::GetNetworkInterfaces()) {
wpi::outs() << " " << addr << '\n';
fmt::print(" {}\n", addr);
}
cs::UsbCamera camera{"usbcam", 0};
camera.SetVideoMode(cs::VideoMode::kMJPEG, 320, 240, 30);
@@ -22,9 +22,8 @@ int main() {
CS_Status status = 0;
cs::AddListener(
[&](const cs::RawEvent& event) {
wpi::outs() << "FPS=" << camera.GetActualFPS()
<< " MBPS=" << (camera.GetActualDataRate() / 1000000.0)
<< '\n';
fmt::print("FPS={} MBPS={}\n", camera.GetActualFPS(),
(camera.GetActualDataRate() / 1000000.0));
},
cs::RawEvent::kTelemetryUpdated, false, &status);
cs::SetTelemetryPeriod(1.0);

View File

@@ -6,12 +6,13 @@
#include <thread>
#include <vector>
#include <fmt/format.h>
#define IMGUI_DEFINE_MATH_OPERATORS
#include <imgui.h>
#include <imgui_internal.h>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc.hpp>
#include <wpi/raw_ostream.h>
#include <wpi/spinlock.h>
#include <wpigui.h>
@@ -38,7 +39,7 @@ int main() {
// get frame from camera
uint64_t time = cvsink.GrabFrame(frame);
if (time == 0) {
wpi::outs() << "error: " << cvsink.GetError() << '\n';
fmt::print("error: {}\n", cvsink.GetError());
continue;
}

View File

@@ -2,9 +2,9 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
import edu.wpi.first.wpiutil.RuntimeDetector;
import edu.wpi.first.util.RuntimeDetector;
public final class DevMain {
/** Main method. */

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
/** A source that represents an Axis IP camera. */
public class AxisCamera extends HttpCamera {

View File

@@ -2,9 +2,9 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
import edu.wpi.first.wpiutil.RuntimeLoader;
import edu.wpi.first.util.RuntimeLoader;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.opencv.core.Core;
@@ -42,7 +42,11 @@ public class CameraServerCvJNI {
}
}
/** Force load the library. */
/**
* Force load the library.
*
* @throws IOException if library load failed
*/
public static synchronized void forceLoad() throws IOException {
if (libraryLoaded) {
return;

View File

@@ -2,10 +2,10 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
import edu.wpi.cscore.raw.RawFrame;
import edu.wpi.first.wpiutil.RuntimeLoader;
import edu.wpi.first.cscore.raw.RawFrame;
import edu.wpi.first.util.RuntimeLoader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -43,7 +43,11 @@ public class CameraServerJNI {
}
}
/** Force load the library. */
/**
* Force load the library.
*
* @throws IOException if library load failed
*/
public static synchronized void forceLoad() throws IOException {
if (libraryLoaded) {
return;

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
import org.opencv.core.Mat;
@@ -38,6 +38,7 @@ public class CvSink extends ImageSink {
* Wait for the next frame and get the image. Times out (returning 0) after 0.225 seconds. The
* provided image will have three 3-bit channels stored in BGR order.
*
* @param image Where to store the image.
* @return Frame time, or 0 on error (call GetError() to obtain the error message)
*/
public long grabFrame(Mat image) {
@@ -48,6 +49,8 @@ public class CvSink extends ImageSink {
* Wait for the next frame and get the image. Times out (returning 0) after timeout seconds. The
* provided image will have three 3-bit channels stored in BGR order.
*
* @param image Where to store the image.
* @param timeout Retrieval timeout in seconds.
* @return Frame time, or 0 on error (call GetError() to obtain the error message); the frame time
* is in 1 us increments.
*/
@@ -59,6 +62,7 @@ public class CvSink extends ImageSink {
* Wait for the next frame and get the image. May block forever. The provided image will have
* three 3-bit channels stored in BGR order.
*
* @param image Where to store the image.
* @return Frame time, or 0 on error (call GetError() to obtain the error message); the frame time
* is in 1 us increments.
*/

View File

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

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
/** A source that represents a MJPEG-over-HTTP (IP) camera. */
public class HttpCamera extends VideoCamera {
@@ -88,17 +88,27 @@ public class HttpCamera extends VideoCamera {
* Get the kind of HTTP camera.
*
* <p>Autodetection can result in returning a different value than the camera was created with.
*
* @return The kind of HTTP camera.
*/
public HttpCameraKind getHttpCameraKind() {
return getHttpCameraKindFromInt(CameraServerJNI.getHttpCameraKind(m_handle));
}
/** Change the URLs used to connect to the camera. */
/**
* Change the URLs used to connect to the camera.
*
* @param urls Array of Camera URLs
*/
public void setUrls(String[] urls) {
CameraServerJNI.setHttpCameraUrls(m_handle, urls);
}
/** Get the URLs used to connect to the camera. */
/**
* Get the URLs used to connect to the camera.
*
* @return Array of camera URLs.
*/
public String[] getUrls() {
return CameraServerJNI.getHttpCameraUrls(m_handle);
}

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
public abstract class ImageSink extends VideoSink {
protected ImageSink(int handle) {
@@ -18,7 +18,11 @@ public abstract class ImageSink extends VideoSink {
CameraServerJNI.setSinkDescription(m_handle, description);
}
/** Get error string. Call this if WaitForFrame() returns 0 to determine what the error is. */
/**
* Get error string. Call this if WaitForFrame() returns 0 to determine what the error is.
*
* @return Error string.
*/
public String getError() {
return CameraServerJNI.getSinkError(m_handle);
}
@@ -27,6 +31,8 @@ public abstract class ImageSink extends VideoSink {
* Enable or disable getting new frames. Disabling will cause processFrame (for callback-based
* CvSinks) to not be called and WaitForFrame() to not return. This can be used to save processor
* resources when frames are not needed.
*
* @param enabled Enable to get new frames.
*/
public void setEnabled(boolean enabled) {
CameraServerJNI.setSinkEnabled(m_handle, enabled);

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
public abstract class ImageSource extends VideoSource {
protected ImageSource(int handle) {
@@ -12,6 +12,8 @@ public abstract class ImageSource extends VideoSource {
/**
* Signal sinks that an error has occurred. This should be called instead of NotifyFrame when an
* error occurs.
*
* @param msg Error message.
*/
public void notifyError(String msg) {
CameraServerJNI.notifySourceError(m_handle, msg);

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
/** A sink that acts as a MJPEG-over-HTTP network server. */
public class MjpegServer extends VideoSink {
@@ -27,12 +27,20 @@ public class MjpegServer extends VideoSink {
this(name, "", port);
}
/** Get the listen address of the server. */
/**
* Get the listen address of the server.
*
* @return The listen address.
*/
public String getListenAddress() {
return CameraServerJNI.getMjpegServerListenAddress(m_handle);
}
/** Get the port number of the server. */
/**
* Get the port number of the server.
*
* @return The port number.
*/
public int getPort() {
return CameraServerJNI.getMjpegServerPort(m_handle);
}

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
/** A source that represents a USB camera. */
public class UsbCamera extends VideoCamera {
@@ -35,17 +35,29 @@ public class UsbCamera extends VideoCamera {
return CameraServerJNI.enumerateUsbCameras();
}
/** Change the path to the device. */
/**
* Change the path to the device.
*
* @param path New device path.
*/
void setPath(String path) {
CameraServerJNI.setUsbCameraPath(m_handle, path);
}
/** Get the path to the device. */
/**
* Get the path to the device.
*
* @return The device path.
*/
public String getPath() {
return CameraServerJNI.getUsbCameraPath(m_handle);
}
/** Get the full camera information for the device. */
/**
* Get the full camera information for the device.
*
* @return The camera information.
*/
public UsbCameraInfo getInfo() {
return CameraServerJNI.getUsbCameraInfo(m_handle);
}

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
/** USB camera information. */
public class UsbCameraInfo {

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
/** A source that represents a video camera. */
public class VideoCamera extends VideoSource {
@@ -18,12 +18,20 @@ public class VideoCamera extends VideoSource {
super(handle);
}
/** Set the brightness, as a percentage (0-100). */
/**
* Set the brightness, as a percentage (0-100).
*
* @param brightness Brightness as a percentage (0-100).
*/
public synchronized void setBrightness(int brightness) {
CameraServerJNI.setCameraBrightness(m_handle, brightness);
}
/** Get the brightness, as a percentage (0-100). */
/**
* Get the brightness, as a percentage (0-100).
*
* @return The brightness as a percentage (0-100).
*/
public synchronized int getBrightness() {
return CameraServerJNI.getCameraBrightness(m_handle);
}
@@ -38,7 +46,11 @@ public class VideoCamera extends VideoSource {
CameraServerJNI.setCameraWhiteBalanceHoldCurrent(m_handle);
}
/** Set the white balance to manual, with specified color temperature. */
/**
* Set the white balance to manual, with specified color temperature.
*
* @param value The specified color temperature.
*/
public synchronized void setWhiteBalanceManual(int value) {
CameraServerJNI.setCameraWhiteBalanceManual(m_handle, value);
}
@@ -53,7 +65,11 @@ public class VideoCamera extends VideoSource {
CameraServerJNI.setCameraExposureHoldCurrent(m_handle);
}
/** Set the exposure to manual, as a percentage (0-100). */
/**
* Set the exposure to manual, as a percentage (0-100).
*
* @param value The exposure as a percentage (0-100).
*/
public synchronized void setExposureManual(int value) {
CameraServerJNI.setCameraExposureManual(m_handle, value);
}

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
/** Video event. */
public class VideoEvent {
@@ -46,7 +46,6 @@ public class VideoEvent {
* @param kind The numerical representation of kind
* @return The kind
*/
@SuppressWarnings("PMD.CyclomaticComplexity")
public static Kind getKindFromInt(int kind) {
switch (kind) {
case 0x0001:
@@ -92,7 +91,6 @@ public class VideoEvent {
}
}
@SuppressWarnings("PMD.ExcessiveParameterList")
VideoEvent(
int kind,
int source,

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
/** An exception raised by the camera server. */
public class VideoException extends RuntimeException {

View File

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

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
/** Video mode. */
public class VideoMode {
@@ -31,7 +31,14 @@ public class VideoMode {
return m_pixelFormatValues[pixelFormat];
}
/** Create a new video mode. */
/**
* Create a new video mode.
*
* @param pixelFormat The pixel format enum as an integer.
* @param width The image width in pixels.
* @param height The image height in pixels.
* @param fps The camera's frames per second.
*/
public VideoMode(int pixelFormat, int width, int height, int fps) {
this.pixelFormat = getPixelFormatFromInt(pixelFormat);
this.width = width;
@@ -39,7 +46,14 @@ public class VideoMode {
this.fps = fps;
}
/** Create a new video mode. */
/**
* Create a new video mode.
*
* @param pixelFormat The pixel format.
* @param width The image width in pixels.
* @param height The image height in pixels.
* @param fps The camera's frames per second.
*/
public VideoMode(PixelFormat pixelFormat, int width, int height, int fps) {
this.pixelFormat = pixelFormat;
this.width = width;

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
/** A source or sink property. */
public class VideoProperty {

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
/**
* A source for video that provides a sequence of frames. Each frame may consist of multiple images
@@ -83,7 +83,11 @@ public class VideoSink implements AutoCloseable {
return m_handle;
}
/** Get the kind of the sink. */
/**
* Get the kind of the sink.
*
* @return The kind of the sink.
*/
public Kind getKind() {
return getKindFromInt(CameraServerJNI.getSinkKind(m_handle));
}
@@ -91,12 +95,18 @@ public class VideoSink implements AutoCloseable {
/**
* Get the name of the sink. The name is an arbitrary identifier provided when the sink is
* created, and should be unique.
*
* @return The name of the sink.
*/
public String getName() {
return CameraServerJNI.getSinkName(m_handle);
}
/** Get the sink description. This is sink-kind specific. */
/**
* Get the sink description. This is sink-kind specific.
*
* @return The sink description.
*/
public String getDescription() {
return CameraServerJNI.getSinkDescription(m_handle);
}
@@ -111,8 +121,11 @@ public class VideoSink implements AutoCloseable {
return new VideoProperty(CameraServerJNI.getSinkProperty(m_handle, name));
}
/** Enumerate all properties of this sink. */
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
/**
* Enumerate all properties of this sink.
*
* @return List of properties.
*/
public VideoProperty[] enumerateProperties() {
int[] handles = CameraServerJNI.enumerateSinkProperties(m_handle);
VideoProperty[] rv = new VideoProperty[handles.length];
@@ -195,7 +208,6 @@ public class VideoSink implements AutoCloseable {
*
* @return Vector of sinks.
*/
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
public static VideoSink[] enumerateSinks() {
int[] handles = CameraServerJNI.enumerateSinks();
VideoSink[] rv = new VideoSink[handles.length];

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore;
package edu.wpi.first.cscore;
/**
* A source for video that provides a sequence of frames. Each frame may consist of multiple images
@@ -113,7 +113,11 @@ public class VideoSource implements AutoCloseable {
return m_handle;
}
/** Get the kind of the source. */
/**
* Get the kind of the source.
*
* @return The kind of the source.
*/
public Kind getKind() {
return getKindFromInt(CameraServerJNI.getSourceKind(m_handle));
}
@@ -121,12 +125,18 @@ public class VideoSource implements AutoCloseable {
/**
* Get the name of the source. The name is an arbitrary identifier provided when the source is
* created, and should be unique.
*
* @return The name of the source.
*/
public String getName() {
return CameraServerJNI.getSourceName(m_handle);
}
/** Get the source description. This is source-kind specific. */
/**
* Get the source description. This is source-kind specific.
*
* @return The source description.
*/
public String getDescription() {
return CameraServerJNI.getSourceDescription(m_handle);
}
@@ -153,7 +163,11 @@ public class VideoSource implements AutoCloseable {
CameraServerJNI.setSourceConnectionStrategy(m_handle, strategy.getValue());
}
/** Returns if the source currently connected to whatever is providing the images. */
/**
* Returns true if the source currently connected to whatever is providing the images.
*
* @return True if the source currently connected to whatever is providing the images.
*/
public boolean isConnected() {
return CameraServerJNI.isSourceConnected(m_handle);
}
@@ -178,8 +192,11 @@ public class VideoSource implements AutoCloseable {
return new VideoProperty(CameraServerJNI.getSourceProperty(m_handle, name));
}
/** Enumerate all properties of this source. */
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
/**
* Enumerate all properties of this source.
*
* @return Array of video properties.
*/
public VideoProperty[] enumerateProperties() {
int[] handles = CameraServerJNI.enumerateSourceProperties(m_handle);
VideoProperty[] rv = new VideoProperty[handles.length];
@@ -189,7 +206,11 @@ public class VideoSource implements AutoCloseable {
return rv;
}
/** Get the current video mode. */
/**
* Get the current video mode.
*
* @return The current video mode.
*/
public VideoMode getVideoMode() {
return CameraServerJNI.getSourceVideoMode(m_handle);
}
@@ -198,6 +219,7 @@ public class VideoSource implements AutoCloseable {
* Set the video mode.
*
* @param mode Video mode
* @return True if set successfully.
*/
public boolean setVideoMode(VideoMode mode) {
return CameraServerJNI.setSourceVideoMode(
@@ -313,7 +335,11 @@ public class VideoSource implements AutoCloseable {
m_handle, CameraServerJNI.TelemetryKind.kSourceBytesReceived);
}
/** Enumerate all known video modes for this source. */
/**
* Enumerate all known video modes for this source.
*
* @return Vector of video modes.
*/
public VideoMode[] enumerateVideoModes() {
return CameraServerJNI.enumerateSourceVideoModes(m_handle);
}
@@ -323,7 +349,6 @@ public class VideoSource implements AutoCloseable {
*
* @return Vector of sinks.
*/
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
public VideoSink[] enumerateSinks() {
int[] handles = CameraServerJNI.enumerateSourceSinks(m_handle);
VideoSink[] rv = new VideoSink[handles.length];
@@ -338,7 +363,6 @@ public class VideoSource implements AutoCloseable {
*
* @return Vector of sources.
*/
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
public static VideoSource[] enumerateSources() {
int[] handles = CameraServerJNI.enumerateSources();
VideoSource[] rv = new VideoSource[handles.length];

View File

@@ -2,9 +2,9 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore.raw;
package edu.wpi.first.cscore.raw;
import edu.wpi.cscore.CameraServerJNI;
import edu.wpi.first.cscore.CameraServerJNI;
import java.nio.ByteBuffer;
/**
@@ -35,7 +35,16 @@ public class RawFrame implements AutoCloseable {
CameraServerJNI.freeRawFrame(m_framePtr);
}
/** Called from JNI to set data in class. */
/**
* Called from JNI to set data in class.
*
* @param dataByteBuffer A ByteBuffer pointing to the frame data.
* @param dataPtr A long (a char* in native code) pointing to the frame data.
* @param totalData The total length of the data stored in the frame.
* @param width The width of the frame.
* @param height The height of the frame.
* @param pixelFormat The PixelFormat of the frame.
*/
public void setData(
ByteBuffer dataByteBuffer,
long dataPtr,
@@ -51,7 +60,11 @@ public class RawFrame implements AutoCloseable {
m_pixelFormat = pixelFormat;
}
/** Get the pointer to native representation of this frame. */
/**
* Get the pointer to native representation of this frame.
*
* @return The pointer to native representation of this frame.
*/
public long getFramePtr() {
return m_framePtr;
}
@@ -60,6 +73,8 @@ public class RawFrame implements AutoCloseable {
* 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 getDataByteBuffer() {
return m_dataByteBuffer;
@@ -69,42 +84,72 @@ public class RawFrame implements AutoCloseable {
* Get a long (is a char* 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 m_dataPtr;
}
/** Get the total length of the data stored in the frame. */
/**
* Get the total length of the data stored in the frame.
*
* @return The total length of the data stored in the frame.
*/
public int getTotalData() {
return m_totalData;
}
/** Get the width of the frame. */
/**
* Get the width of the frame.
*
* @return The width of the frame.
*/
public int getWidth() {
return m_width;
}
/** Set the width of the frame. */
/**
* Set the width of the frame.
*
* @param width The width of the frame.
*/
public void setWidth(int width) {
this.m_width = width;
}
/** Get the height of the frame. */
/**
* Get the height of the frame.
*
* @return The height of the frame.
*/
public int getHeight() {
return m_height;
}
/** Set the height of the frame. */
/**
* Set the height of the frame.
*
* @param height The height of the frame.
*/
public void setHeight(int height) {
this.m_height = height;
}
/** Get the PixelFormat of the frame. */
/**
* Get the PixelFormat of the frame.
*
* @return The PixelFormat of the frame.
*/
public int getPixelFormat() {
return m_pixelFormat;
}
/** Set the PixelFormat of the frame. */
/**
* Set the PixelFormat of the frame.
*
* @param pixelFormat The PixelFormat of the frame.
*/
public void setPixelFormat(int pixelFormat) {
this.m_pixelFormat = pixelFormat;
}

View File

@@ -2,10 +2,10 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore.raw;
package edu.wpi.first.cscore.raw;
import edu.wpi.cscore.CameraServerJNI;
import edu.wpi.cscore.ImageSink;
import edu.wpi.first.cscore.CameraServerJNI;
import edu.wpi.first.cscore.ImageSink;
/**
* A sink for user code to accept video frames as raw bytes.
@@ -28,6 +28,7 @@ public class RawSink extends ImageSink {
* Wait for the next frame and get the image. Times out (returning 0) after 0.225 seconds. The
* provided image will have three 8-bit channels stored in BGR order.
*
* @param frame The frame object in which to store the image.
* @return Frame time, or 0 on error (call getError() to obtain the error message); the frame time
* is in the same time base as wpi::Now(), and is in 1 us increments.
*/
@@ -39,6 +40,8 @@ public class RawSink extends ImageSink {
* Wait for the next frame and get the image. Times out (returning 0) after timeout seconds. The
* provided image will have three 8-bit channels stored in BGR order.
*
* @param frame The frame object in which to store the image.
* @param timeout The frame timeout in seconds.
* @return Frame time, or 0 on error (call getError() to obtain the error message); the frame time
* is in the same time base as wpi::Now(), and is in 1 us increments.
*/
@@ -50,6 +53,7 @@ public class RawSink extends ImageSink {
* Wait for the next frame and get the image. May block forever. The provided image will have
* three 8-bit channels stored in BGR order.
*
* @param frame The frame object in which to store the image.
* @return Frame time, or 0 on error (call getError() to obtain the error message); the frame time
* is in the same time base as wpi::Now(), and is in 1 us increments.
*/

View File

@@ -2,11 +2,11 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.cscore.raw;
package edu.wpi.first.cscore.raw;
import edu.wpi.cscore.CameraServerJNI;
import edu.wpi.cscore.ImageSource;
import edu.wpi.cscore.VideoMode;
import edu.wpi.first.cscore.CameraServerJNI;
import edu.wpi.first.cscore.ImageSource;
import edu.wpi.first.cscore.VideoMode;
/**
* A source for user code to provide video frames as raw bytes.

View File

@@ -13,7 +13,7 @@
using namespace cs;
ConfigurableSourceImpl::ConfigurableSourceImpl(const wpi::Twine& name,
ConfigurableSourceImpl::ConfigurableSourceImpl(std::string_view name,
wpi::Logger& logger,
Notifier& notifier,
Telemetry& telemetry,
@@ -50,11 +50,11 @@ void ConfigurableSourceImpl::NumSinksEnabledChanged() {
// ignore
}
void ConfigurableSourceImpl::NotifyError(const wpi::Twine& msg) {
void ConfigurableSourceImpl::NotifyError(std::string_view msg) {
PutError(msg, wpi::Now());
}
int ConfigurableSourceImpl::CreateProperty(const wpi::Twine& name,
int ConfigurableSourceImpl::CreateProperty(std::string_view name,
CS_PropertyKind kind, int minimum,
int maximum, int step,
int defaultValue, int value) {
@@ -75,12 +75,12 @@ int ConfigurableSourceImpl::CreateProperty(const wpi::Twine& name,
value = prop.value;
});
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CREATED, name, ndx,
kind, value, wpi::Twine{});
kind, value, {});
return ndx;
}
int ConfigurableSourceImpl::CreateProperty(
const wpi::Twine& name, CS_PropertyKind kind, int minimum, int maximum,
std::string_view name, CS_PropertyKind kind, int minimum, int maximum,
int step, int defaultValue, int value,
std::function<void(CS_Property property)> onChange) {
// TODO
@@ -88,7 +88,7 @@ int ConfigurableSourceImpl::CreateProperty(
}
void ConfigurableSourceImpl::SetEnumPropertyChoices(
int property, wpi::ArrayRef<std::string> choices, CS_Status* status) {
int property, wpi::span<const std::string> choices, CS_Status* status) {
std::scoped_lock lock(m_mutex);
auto prop = GetProperty(property);
if (!prop) {
@@ -99,8 +99,8 @@ void ConfigurableSourceImpl::SetEnumPropertyChoices(
*status = CS_WRONG_PROPERTY_TYPE;
return;
}
prop->enumChoices = choices;
prop->enumChoices.assign(choices.begin(), choices.end());
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CHOICES_UPDATED,
prop->name, property, CS_PROP_ENUM,
prop->value, wpi::Twine{});
prop->value, {});
}

View File

@@ -9,10 +9,10 @@
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include <wpi/ArrayRef.h>
#include <wpi/Twine.h>
#include <wpi/span.h>
#include "SourceImpl.h"
@@ -20,7 +20,7 @@ namespace cs {
class ConfigurableSourceImpl : public SourceImpl {
protected:
ConfigurableSourceImpl(const wpi::Twine& name, wpi::Logger& logger,
ConfigurableSourceImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry,
const VideoMode& mode);
@@ -35,13 +35,14 @@ class ConfigurableSourceImpl : public SourceImpl {
void NumSinksEnabledChanged() override;
// OpenCV-specific functions
void NotifyError(const wpi::Twine& msg);
int CreateProperty(const wpi::Twine& name, CS_PropertyKind kind, int minimum,
void NotifyError(std::string_view msg);
int CreateProperty(std::string_view name, CS_PropertyKind kind, int minimum,
int maximum, int step, int defaultValue, int value);
int CreateProperty(const wpi::Twine& name, CS_PropertyKind kind, int minimum,
int CreateProperty(std::string_view name, CS_PropertyKind kind, int minimum,
int maximum, int step, int defaultValue, int value,
std::function<void(CS_Property property)> onChange);
void SetEnumPropertyChoices(int property, wpi::ArrayRef<std::string> choices,
void SetEnumPropertyChoices(int property,
wpi::span<const std::string> choices,
CS_Status* status);
private:

View File

@@ -18,14 +18,14 @@
using namespace cs;
CvSinkImpl::CvSinkImpl(const wpi::Twine& name, wpi::Logger& logger,
CvSinkImpl::CvSinkImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry)
: SinkImpl{name, logger, notifier, telemetry} {
m_active = true;
// m_thread = std::thread(&CvSinkImpl::ThreadMain, this);
}
CvSinkImpl::CvSinkImpl(const wpi::Twine& name, wpi::Logger& logger,
CvSinkImpl::CvSinkImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry,
std::function<void(uint64_t time)> processFrame)
: SinkImpl{name, logger, notifier, telemetry} {}
@@ -110,7 +110,7 @@ void CvSinkImpl::ThreadMain() {
std::this_thread::sleep_for(std::chrono::seconds(1));
continue;
}
SDEBUG4("waiting for frame");
SDEBUG4("{}", "waiting for frame");
Frame frame = source->GetNextFrame(); // blocks
if (!m_active) {
break;
@@ -127,14 +127,14 @@ void CvSinkImpl::ThreadMain() {
namespace cs {
CS_Sink CreateCvSink(const wpi::Twine& name, CS_Status* status) {
CS_Sink CreateCvSink(std::string_view name, CS_Status* status) {
auto& inst = Instance::GetInstance();
return inst.CreateSink(
CS_SINK_CV, std::make_shared<CvSinkImpl>(name, inst.logger, inst.notifier,
inst.telemetry));
}
CS_Sink CreateCvSinkCallback(const wpi::Twine& name,
CS_Sink CreateCvSinkCallback(std::string_view name,
std::function<void(uint64_t time)> processFrame,
CS_Status* status) {
auto& inst = Instance::GetInstance();
@@ -145,7 +145,7 @@ CS_Sink CreateCvSinkCallback(const wpi::Twine& name,
static constexpr unsigned SinkMask = CS_SINK_CV | CS_SINK_RAW;
void SetSinkDescription(CS_Sink sink, const wpi::Twine& description,
void SetSinkDescription(CS_Sink sink, std::string_view description,
CS_Status* status) {
auto data = Instance::GetInstance().GetSink(sink);
if (!data || (data->kind & SinkMask) == 0) {
@@ -183,12 +183,12 @@ std::string GetSinkError(CS_Sink sink, CS_Status* status) {
return static_cast<CvSinkImpl&>(*data->sink).GetError();
}
wpi::StringRef GetSinkError(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
CS_Status* status) {
std::string_view GetSinkError(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
CS_Status* status) {
auto data = Instance::GetInstance().GetSink(sink);
if (!data || (data->kind & SinkMask) == 0) {
*status = CS_INVALID_HANDLE;
return wpi::StringRef{};
return {};
}
return static_cast<CvSinkImpl&>(*data->sink).GetError(buf);
}

View File

@@ -9,10 +9,10 @@
#include <atomic>
#include <functional>
#include <string_view>
#include <thread>
#include <opencv2/core/core.hpp>
#include <wpi/Twine.h>
#include <wpi/condition_variable.h>
#include "Frame.h"
@@ -24,9 +24,9 @@ class SourceImpl;
class CvSinkImpl : public SinkImpl {
public:
CvSinkImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
CvSinkImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry);
CvSinkImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
CvSinkImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry,
std::function<void(uint64_t time)> processFrame);
~CvSinkImpl() override;

View File

@@ -7,7 +7,6 @@
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <wpi/STLExtras.h>
#include <wpi/timestamp.h>
#include "Handle.h"
@@ -19,7 +18,7 @@
using namespace cs;
CvSourceImpl::CvSourceImpl(const wpi::Twine& name, wpi::Logger& logger,
CvSourceImpl::CvSourceImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry,
const VideoMode& mode)
: ConfigurableSourceImpl{name, logger, notifier, telemetry, mode} {}
@@ -53,8 +52,7 @@ void CvSourceImpl::PutFrame(cv::Mat& image) {
cv::cvtColor(finalImage, dest->AsMat(), cv::COLOR_BGRA2BGR);
break;
default:
SERROR("PutFrame: " << image.channels()
<< "-channel images not supported");
SERROR("PutFrame: {}-channel images not supported", image.channels());
return;
}
SourceImpl::PutFrame(std::move(dest), wpi::Now());
@@ -62,7 +60,7 @@ void CvSourceImpl::PutFrame(cv::Mat& image) {
namespace cs {
CS_Source CreateCvSource(const wpi::Twine& name, const VideoMode& mode,
CS_Source CreateCvSource(std::string_view name, const VideoMode& mode,
CS_Status* status) {
auto& inst = Instance::GetInstance();
return inst.CreateSource(CS_SOURCE_CV, std::make_shared<CvSourceImpl>(
@@ -81,7 +79,7 @@ void PutSourceFrame(CS_Source source, cv::Mat& image, CS_Status* status) {
static constexpr unsigned SourceMask = CS_SINK_CV | CS_SINK_RAW;
void NotifySourceError(CS_Source source, const wpi::Twine& msg,
void NotifySourceError(CS_Source source, std::string_view msg,
CS_Status* status) {
auto data = Instance::GetInstance().GetSource(source);
if (!data || (data->kind & SourceMask) == 0) {
@@ -100,7 +98,7 @@ void SetSourceConnected(CS_Source source, bool connected, CS_Status* status) {
static_cast<CvSourceImpl&>(*data->source).SetConnected(connected);
}
void SetSourceDescription(CS_Source source, const wpi::Twine& description,
void SetSourceDescription(CS_Source source, std::string_view description,
CS_Status* status) {
auto data = Instance::GetInstance().GetSource(source);
if (!data || (data->kind & SourceMask) == 0) {
@@ -110,7 +108,7 @@ void SetSourceDescription(CS_Source source, const wpi::Twine& description,
static_cast<CvSourceImpl&>(*data->source).SetDescription(description);
}
CS_Property CreateSourceProperty(CS_Source source, const wpi::Twine& name,
CS_Property CreateSourceProperty(CS_Source source, std::string_view name,
CS_PropertyKind kind, int minimum, int maximum,
int step, int defaultValue, int value,
CS_Status* status) {
@@ -126,7 +124,7 @@ CS_Property CreateSourceProperty(CS_Source source, const wpi::Twine& name,
}
CS_Property CreateSourcePropertyCallback(
CS_Source source, const wpi::Twine& name, CS_PropertyKind kind, int minimum,
CS_Source source, std::string_view name, CS_PropertyKind kind, int minimum,
int maximum, int step, int defaultValue, int value,
std::function<void(CS_Property property)> onChange, CS_Status* status) {
auto data = Instance::GetInstance().GetSource(source);
@@ -141,7 +139,7 @@ CS_Property CreateSourcePropertyCallback(
}
void SetSourceEnumPropertyChoices(CS_Source source, CS_Property property,
wpi::ArrayRef<std::string> choices,
wpi::span<const std::string> choices,
CS_Status* status) {
auto data = Instance::GetInstance().GetSource(source);
if (!data || (data->kind & SourceMask) == 0) {

View File

@@ -9,11 +9,10 @@
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include <opencv2/core/core.hpp>
#include <wpi/ArrayRef.h>
#include <wpi/Twine.h>
#include "ConfigurableSourceImpl.h"
#include "SourceImpl.h"
@@ -22,7 +21,7 @@ namespace cs {
class CvSourceImpl : public ConfigurableSourceImpl {
public:
CvSourceImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
CvSourceImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry, const VideoMode& mode);
~CvSourceImpl() override;

View File

@@ -16,10 +16,10 @@
using namespace cs;
Frame::Frame(SourceImpl& source, const wpi::Twine& error, Time time)
Frame::Frame(SourceImpl& source, std::string_view error, Time time)
: m_impl{source.AllocFrameImpl().release()} {
m_impl->refcount = 1;
m_impl->error = error.str();
m_impl->error = error;
m_impl->time = time;
}
@@ -522,10 +522,8 @@ Image* Frame::GetImageImpl(int width, int height,
}
WPI_DEBUG4(Instance::GetInstance().logger,
"converting image from " << cur->width << "x" << cur->height
<< " type " << cur->pixelFormat << " to "
<< width << "x" << height << " type "
<< pixelFormat);
"converting image from {}x{} type {} to {}x{} type {}", cur->width,
cur->height, cur->pixelFormat, width, height, pixelFormat);
// If the source image is a JPEG, we need to decode it before we can do
// anything else with it. Note that if the destination format is JPEG, we

View File

@@ -8,11 +8,11 @@
#include <atomic>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <wpi/SmallVector.h>
#include <wpi/Twine.h>
#include <wpi/mutex.h>
#include "Image.h"
@@ -44,7 +44,7 @@ class Frame {
public:
Frame() noexcept = default;
Frame(SourceImpl& source, const wpi::Twine& error, Time time);
Frame(SourceImpl& source, std::string_view error, Time time);
Frame(SourceImpl& source, std::unique_ptr<Image> image, Time time);
@@ -72,7 +72,7 @@ class Frame {
Time GetTime() const { return m_impl ? m_impl->time : 0; }
wpi::StringRef GetError() const {
std::string_view GetError() const {
if (!m_impl) {
return {};
}

View File

@@ -5,6 +5,7 @@
#include "HttpCameraImpl.h"
#include <wpi/MemAlloc.h>
#include <wpi/StringExtras.h>
#include <wpi/TCPConnector.h>
#include <wpi/timestamp.h>
@@ -18,7 +19,7 @@
using namespace cs;
HttpCameraImpl::HttpCameraImpl(const wpi::Twine& name, CS_HttpCameraKind kind,
HttpCameraImpl::HttpCameraImpl(std::string_view name, CS_HttpCameraKind kind,
wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry)
: SourceImpl{name, logger, notifier, telemetry}, m_kind{kind} {}
@@ -84,7 +85,7 @@ void HttpCameraImpl::MonitorThreadMain() {
// (this will result in an error at the read point, and ultimately
// a reconnect attempt)
if (m_streamConn && m_frameCount == 0) {
SWARNING("Monitor detected stream hung, disconnecting");
SWARNING("{}", "Monitor detected stream hung, disconnecting");
m_streamConn->stream->close();
}
@@ -92,7 +93,7 @@ void HttpCameraImpl::MonitorThreadMain() {
m_frameCount = 0;
}
SDEBUG("Monitor Thread exiting");
SDEBUG("{}", "Monitor Thread exiting");
}
void HttpCameraImpl::StreamThreadMain() {
@@ -132,14 +133,14 @@ void HttpCameraImpl::StreamThreadMain() {
SetConnected(true);
// stream
DeviceStream(conn->is, boundary);
DeviceStream(conn->is, boundary.str());
{
std::unique_lock lock(m_mutex);
m_streamConn = nullptr;
}
}
SDEBUG("Camera Thread exiting");
SDEBUG("{}", "Camera Thread exiting");
SetConnected(false);
}
@@ -150,7 +151,7 @@ wpi::HttpConnection* HttpCameraImpl::DeviceStreamConnect(
{
std::scoped_lock lock(m_mutex);
if (m_locations.empty()) {
SERROR("locations array is empty!?");
SERROR("{}", "locations array is empty!?");
std::this_thread::sleep_for(std::chrono::seconds(1));
return nullptr;
}
@@ -181,19 +182,17 @@ wpi::HttpConnection* HttpCameraImpl::DeviceStreamConnect(
std::string warn;
if (!conn->Handshake(req, &warn)) {
SWARNING(GetName() << ": " << warn);
SWARNING("{}", warn);
std::scoped_lock lock(m_mutex);
m_streamConn = nullptr;
return nullptr;
}
// Parse Content-Type header to get the boundary
wpi::StringRef mediaType, contentType;
std::tie(mediaType, contentType) = conn->contentType.str().split(';');
mediaType = mediaType.trim();
auto [mediaType, contentType] = wpi::split(conn->contentType.str(), ';');
mediaType = wpi::trim(mediaType);
if (mediaType != "multipart/x-mixed-replace") {
SWARNING("\"" << req.host << "\": unrecognized Content-Type \"" << mediaType
<< "\"");
SWARNING("\"{}\": unrecognized Content-Type \"{}\"", req.host, mediaType);
std::scoped_lock lock(m_mutex);
m_streamConn = nullptr;
return nullptr;
@@ -202,14 +201,13 @@ wpi::HttpConnection* HttpCameraImpl::DeviceStreamConnect(
// media parameters
boundary.clear();
while (!contentType.empty()) {
wpi::StringRef keyvalue;
std::tie(keyvalue, contentType) = contentType.split(';');
contentType = contentType.ltrim();
wpi::StringRef key, value;
std::tie(key, value) = keyvalue.split('=');
if (key.trim() == "boundary") {
value = value.trim().trim('"'); // value may be quoted
if (value.startswith("--")) {
std::string_view keyvalue;
std::tie(keyvalue, contentType) = wpi::split(contentType, ';');
contentType = wpi::ltrim(contentType);
auto [key, value] = wpi::split(keyvalue, '=');
if (wpi::trim(key) == "boundary") {
value = wpi::trim(wpi::trim(value), '"'); // value may be quoted
if (wpi::starts_with(value, "--")) {
value = value.substr(2);
}
boundary.append(value.begin(), value.end());
@@ -217,8 +215,7 @@ wpi::HttpConnection* HttpCameraImpl::DeviceStreamConnect(
}
if (boundary.empty()) {
SWARNING("\"" << req.host
<< "\": empty multi-part boundary or no Content-Type");
SWARNING("\"{}\": empty multi-part boundary or no Content-Type", req.host);
std::scoped_lock lock(m_mutex);
m_streamConn = nullptr;
return nullptr;
@@ -228,7 +225,7 @@ wpi::HttpConnection* HttpCameraImpl::DeviceStreamConnect(
}
void HttpCameraImpl::DeviceStream(wpi::raw_istream& is,
wpi::StringRef boundary) {
std::string_view boundary) {
// Stored here so we reuse it from frame to frame
std::string imageBuf;
@@ -275,28 +272,29 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::raw_istream& is,
wpi::SmallString<64> contentTypeBuf;
wpi::SmallString<64> contentLengthBuf;
if (!ParseHttpHeaders(is, &contentTypeBuf, &contentLengthBuf)) {
SWARNING("disconnected during headers");
SWARNING("{}", "disconnected during headers");
PutError("disconnected during headers", wpi::Now());
return false;
}
// Check the content type (if present)
if (!contentTypeBuf.str().empty() &&
!contentTypeBuf.str().startswith("image/jpeg")) {
wpi::SmallString<64> errBuf;
wpi::raw_svector_ostream errMsg{errBuf};
errMsg << "received unknown Content-Type \"" << contentTypeBuf << "\"";
SWARNING(errMsg.str());
PutError(errMsg.str(), wpi::Now());
!wpi::starts_with(contentTypeBuf, "image/jpeg")) {
auto errMsg =
fmt::format("received unknown Content-Type \"{}\"", contentTypeBuf);
SWARNING("{}", errMsg);
PutError(errMsg, wpi::Now());
return false;
}
unsigned int contentLength = 0;
if (contentLengthBuf.str().getAsInteger(10, contentLength)) {
if (auto v = wpi::parse_integer<unsigned int>(contentLengthBuf, 10)) {
contentLength = v.value();
} else {
// Ugh, no Content-Length? Read the blocks of the JPEG file.
int width, height;
if (!ReadJpeg(is, imageBuf, &width, &height)) {
SWARNING("did not receive a JPEG image");
SWARNING("{}", "did not receive a JPEG image");
PutError("did not receive a JPEG image", wpi::Now());
return false;
}
@@ -315,7 +313,7 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::raw_istream& is,
}
int width, height;
if (!GetJpegSize(image->str(), &width, &height)) {
SWARNING("did not receive a JPEG image");
SWARNING("{}", "did not receive a JPEG image");
PutError("did not receive a JPEG image", wpi::Now());
return false;
}
@@ -345,7 +343,7 @@ void HttpCameraImpl::SettingsThreadMain() {
DeviceSendSettings(req);
}
SDEBUG("Settings Thread exiting");
SDEBUG("{}", "Settings Thread exiting");
}
void HttpCameraImpl::DeviceSendSettings(wpi::HttpRequest& req) {
@@ -369,7 +367,7 @@ void HttpCameraImpl::DeviceSendSettings(wpi::HttpRequest& req) {
// Just need a handshake as settings are sent via GET parameters
std::string warn;
if (!conn->Handshake(req, &warn)) {
SWARNING(GetName() << ": " << warn);
SWARNING("{}", warn);
}
conn->stream->close();
@@ -380,7 +378,7 @@ CS_HttpCameraKind HttpCameraImpl::GetKind() const {
return m_kind;
}
bool HttpCameraImpl::SetUrls(wpi::ArrayRef<std::string> urls,
bool HttpCameraImpl::SetUrls(wpi::span<const std::string> urls,
CS_Status* status) {
std::vector<wpi::HttpLocation> locations;
for (const auto& url : urls) {
@@ -388,7 +386,7 @@ bool HttpCameraImpl::SetUrls(wpi::ArrayRef<std::string> urls,
std::string errorMsg;
locations.emplace_back(url, &error, &errorMsg);
if (error) {
SERROR(GetName() << ": " << errorMsg);
SERROR("{}", errorMsg);
*status = CS_BAD_URL;
return false;
}
@@ -410,8 +408,8 @@ std::vector<std::string> HttpCameraImpl::GetUrls() const {
return urls;
}
void HttpCameraImpl::CreateProperty(const wpi::Twine& name,
const wpi::Twine& httpParam,
void HttpCameraImpl::CreateProperty(std::string_view name,
std::string_view httpParam,
bool viaSettings, CS_PropertyKind kind,
int minimum, int maximum, int step,
int defaultValue, int value) const {
@@ -421,13 +419,12 @@ void HttpCameraImpl::CreateProperty(const wpi::Twine& name,
value));
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CREATED, name,
m_propertyData.size() + 1, kind, value,
wpi::Twine{});
m_propertyData.size() + 1, kind, value, {});
}
template <typename T>
void HttpCameraImpl::CreateEnumProperty(
const wpi::Twine& name, const wpi::Twine& httpParam, bool viaSettings,
std::string_view name, std::string_view httpParam, bool viaSettings,
int defaultValue, int value, std::initializer_list<T> choices) const {
std::scoped_lock lock(m_mutex);
m_propertyData.emplace_back(std::make_unique<PropertyData>(
@@ -442,14 +439,14 @@ void HttpCameraImpl::CreateEnumProperty(
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CREATED, name,
m_propertyData.size() + 1, CS_PROP_ENUM,
value, wpi::Twine{});
value, {});
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CHOICES_UPDATED,
name, m_propertyData.size() + 1, CS_PROP_ENUM,
value, wpi::Twine{});
value, {});
}
std::unique_ptr<PropertyImpl> HttpCameraImpl::CreateEmptyProperty(
const wpi::Twine& name) const {
std::string_view name) const {
return std::make_unique<PropertyData>(name);
}
@@ -474,7 +471,7 @@ void HttpCameraImpl::SetProperty(int property, int value, CS_Status* status) {
// TODO
}
void HttpCameraImpl::SetStringProperty(int property, const wpi::Twine& value,
void HttpCameraImpl::SetStringProperty(int property, std::string_view value,
CS_Status* status) {
// TODO
}
@@ -560,7 +557,7 @@ bool AxisCameraImpl::CacheProperties(CS_Status* status) const {
namespace cs {
CS_Source CreateHttpCamera(const wpi::Twine& name, const wpi::Twine& url,
CS_Source CreateHttpCamera(std::string_view name, std::string_view url,
CS_HttpCameraKind kind, CS_Status* status) {
auto& inst = Instance::GetInstance();
std::shared_ptr<HttpCameraImpl> source;
@@ -574,14 +571,15 @@ CS_Source CreateHttpCamera(const wpi::Twine& name, const wpi::Twine& url,
inst.notifier, inst.telemetry);
break;
}
if (!source->SetUrls(url.str(), status)) {
std::string urlStr{url};
if (!source->SetUrls(wpi::span{&urlStr, 1}, status)) {
return 0;
}
return inst.CreateSource(CS_SOURCE_HTTP, source);
}
CS_Source CreateHttpCamera(const wpi::Twine& name,
wpi::ArrayRef<std::string> urls,
CS_Source CreateHttpCamera(std::string_view name,
wpi::span<const std::string> urls,
CS_HttpCameraKind kind, CS_Status* status) {
auto& inst = Instance::GetInstance();
if (urls.empty()) {
@@ -605,7 +603,7 @@ CS_HttpCameraKind GetHttpCameraKind(CS_Source source, CS_Status* status) {
return static_cast<HttpCameraImpl&>(*data->source).GetKind();
}
void SetHttpCameraUrls(CS_Source source, wpi::ArrayRef<std::string> urls,
void SetHttpCameraUrls(CS_Source source, wpi::span<const std::string> urls,
CS_Status* status) {
if (urls.empty()) {
*status = CS_EMPTY_VALUE;

View File

@@ -10,15 +10,16 @@
#include <initializer_list>
#include <memory>
#include <string>
#include <string_view>
#include <thread>
#include <vector>
#include <wpi/HttpUtil.h>
#include <wpi/SmallString.h>
#include <wpi/StringMap.h>
#include <wpi/Twine.h>
#include <wpi/condition_variable.h>
#include <wpi/raw_istream.h>
#include <wpi/span.h>
#include "SourceImpl.h"
#include "cscore_cpp.h"
@@ -27,7 +28,7 @@ namespace cs {
class HttpCameraImpl : public SourceImpl {
public:
HttpCameraImpl(const wpi::Twine& name, CS_HttpCameraKind kind,
HttpCameraImpl(std::string_view name, CS_HttpCameraKind kind,
wpi::Logger& logger, Notifier& notifier, Telemetry& telemetry);
~HttpCameraImpl() override;
@@ -35,7 +36,7 @@ class HttpCameraImpl : public SourceImpl {
// Property functions
void SetProperty(int property, int value, CS_Status* status) override;
void SetStringProperty(int property, const wpi::Twine& value,
void SetStringProperty(int property, std::string_view value,
CS_Status* status) override;
// Standard common camera properties
@@ -54,20 +55,20 @@ class HttpCameraImpl : public SourceImpl {
void NumSinksEnabledChanged() override;
CS_HttpCameraKind GetKind() const;
bool SetUrls(wpi::ArrayRef<std::string> urls, CS_Status* status);
bool SetUrls(wpi::span<const std::string> urls, CS_Status* status);
std::vector<std::string> GetUrls() const;
// Property data
class PropertyData : public PropertyImpl {
public:
PropertyData() = default;
explicit PropertyData(const wpi::Twine& name_) : PropertyImpl{name_} {}
PropertyData(const wpi::Twine& name_, const wpi::Twine& httpParam_,
explicit PropertyData(std::string_view name_) : PropertyImpl{name_} {}
PropertyData(std::string_view name_, std::string_view httpParam_,
bool viaSettings_, CS_PropertyKind kind_, int minimum_,
int maximum_, int step_, int defaultValue_, int value_)
: PropertyImpl(name_, kind_, step_, defaultValue_, value_),
viaSettings(viaSettings_),
httpParam(httpParam_.str()) {
httpParam(httpParam_) {
hasMinimum = true;
minimum = minimum_;
hasMaximum = true;
@@ -81,16 +82,16 @@ class HttpCameraImpl : public SourceImpl {
protected:
std::unique_ptr<PropertyImpl> CreateEmptyProperty(
const wpi::Twine& name) const override;
std::string_view name) const override;
bool CacheProperties(CS_Status* status) const override;
void CreateProperty(const wpi::Twine& name, const wpi::Twine& httpParam,
void CreateProperty(std::string_view name, std::string_view httpParam,
bool viaSettings, CS_PropertyKind kind, int minimum,
int maximum, int step, int defaultValue, int value) const;
template <typename T>
void CreateEnumProperty(const wpi::Twine& name, const wpi::Twine& httpParam,
void CreateEnumProperty(std::string_view name, std::string_view httpParam,
bool viaSettings, int defaultValue, int value,
std::initializer_list<T> choices) const;
@@ -101,7 +102,7 @@ class HttpCameraImpl : public SourceImpl {
// Functions used by StreamThreadMain()
wpi::HttpConnection* DeviceStreamConnect(
wpi::SmallVectorImpl<char>& boundary);
void DeviceStream(wpi::raw_istream& is, wpi::StringRef boundary);
void DeviceStream(wpi::raw_istream& is, std::string_view boundary);
bool DeviceStreamFrame(wpi::raw_istream& is, std::string& imageBuf);
// The camera settings thread
@@ -146,12 +147,12 @@ class HttpCameraImpl : public SourceImpl {
class AxisCameraImpl : public HttpCameraImpl {
public:
AxisCameraImpl(const wpi::Twine& name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry)
AxisCameraImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry)
: HttpCameraImpl{name, CS_HTTP_AXIS, logger, notifier, telemetry} {}
#if 0
void SetProperty(int property, int value, CS_Status* status) override;
void SetStringProperty(int property, const wpi::Twine& value,
void SetStringProperty(int property, std::string_view value,
CS_Status* status) override;
#endif
protected:

View File

@@ -5,10 +5,10 @@
#ifndef CSCORE_IMAGE_H_
#define CSCORE_IMAGE_H_
#include <string_view>
#include <vector>
#include <opencv2/core/core.hpp>
#include <wpi/StringRef.h>
#include "cscore_cpp.h"
#include "default_init_allocator.h"
@@ -34,8 +34,8 @@ class Image {
Image& operator=(const Image&) = delete;
// Getters
operator wpi::StringRef() const { return str(); } // NOLINT
wpi::StringRef str() const { return wpi::StringRef(data(), size()); }
operator std::string_view() const { return str(); } // NOLINT
std::string_view str() const { return {data(), size()}; }
size_t capacity() const { return m_data.capacity(); }
const char* data() const {
return reinterpret_cast<const char*>(m_data.data());

View File

@@ -4,36 +4,32 @@
#include "Instance.h"
#include <wpi/Path.h>
#include <wpi/SmallString.h>
#include <wpi/StringRef.h>
#include <wpi/raw_ostream.h>
#include <string_view>
#include <fmt/format.h>
#include <wpi/fs.h>
using namespace cs;
static void def_log_func(unsigned int level, const char* file,
unsigned int line, const char* msg) {
wpi::SmallString<128> buf;
wpi::raw_svector_ostream oss(buf);
if (level == 20) {
oss << "CS: " << msg << '\n';
wpi::errs() << oss.str();
fmt::print(stderr, "CS: {}\n", msg);
return;
}
wpi::StringRef levelmsg;
std::string_view levelmsg;
if (level >= 50) {
levelmsg = "CRITICAL: ";
levelmsg = "CRITICAL";
} else if (level >= 40) {
levelmsg = "ERROR: ";
levelmsg = "ERROR";
} else if (level >= 30) {
levelmsg = "WARNING: ";
levelmsg = "WARNING";
} else {
return;
}
oss << "CS: " << levelmsg << msg << " (" << wpi::sys::path::filename(file)
<< ':' << line << ")\n";
wpi::errs() << oss.str();
fmt::print(stderr, "CS: {}: {} ({}:{})\n", levelmsg, msg,
fs::path{file}.filename().string(), line);
}
Instance::Instance()

View File

@@ -86,18 +86,17 @@ class Instance {
void DestroySource(CS_Source handle);
void DestroySink(CS_Sink handle);
wpi::ArrayRef<CS_Source> EnumerateSourceHandles(
wpi::span<CS_Source> EnumerateSourceHandles(
wpi::SmallVectorImpl<CS_Source>& vec) {
return m_sources.GetAll(vec);
}
wpi::ArrayRef<CS_Sink> EnumerateSinkHandles(
wpi::SmallVectorImpl<CS_Sink>& vec) {
wpi::span<CS_Sink> EnumerateSinkHandles(wpi::SmallVectorImpl<CS_Sink>& vec) {
return m_sinks.GetAll(vec);
}
wpi::ArrayRef<CS_Sink> EnumerateSourceSinks(
CS_Source source, wpi::SmallVectorImpl<CS_Sink>& vec) {
wpi::span<CS_Sink> EnumerateSourceSinks(CS_Source source,
wpi::SmallVectorImpl<CS_Sink>& vec) {
vec.clear();
m_sinks.ForEach([&](CS_Sink sinkHandle, const SinkData& data) {
if (source == data.sourceHandle.load()) {

View File

@@ -46,20 +46,20 @@ static const unsigned char dhtData[] = {
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa};
bool IsJpeg(wpi::StringRef data) {
bool IsJpeg(std::string_view data) {
if (data.size() < 11) {
return false;
}
// Check for valid SOI
auto bytes = data.bytes_begin();
auto bytes = reinterpret_cast<const unsigned char*>(data.data());
if (bytes[0] != 0xff || bytes[1] != 0xd8) {
return false;
}
return true;
}
bool GetJpegSize(wpi::StringRef data, int* width, int* height) {
bool GetJpegSize(std::string_view data, int* width, int* height) {
if (!IsJpeg(data)) {
return false;
}
@@ -69,7 +69,7 @@ bool GetJpegSize(wpi::StringRef data, int* width, int* height) {
if (data.size() < 4) {
return false; // EOF
}
auto bytes = data.bytes_begin();
auto bytes = reinterpret_cast<const unsigned char*>(data.data());
if (bytes[0] != 0xff) {
return false; // not a tag
}
@@ -94,7 +94,7 @@ bool GetJpegSize(wpi::StringRef data, int* width, int* height) {
}
bool JpegNeedsDHT(const char* data, size_t* size, size_t* locSOF) {
wpi::StringRef sdata(data, *size);
std::string_view sdata(data, *size);
if (!IsJpeg(sdata)) {
return false;
}
@@ -107,7 +107,7 @@ bool JpegNeedsDHT(const char* data, size_t* size, size_t* locSOF) {
if (sdata.size() < 4) {
return false; // EOF
}
auto bytes = sdata.bytes_begin();
auto bytes = reinterpret_cast<const unsigned char*>(sdata.data());
if (bytes[0] != 0xff) {
return false; // not a tag
}
@@ -132,9 +132,8 @@ bool JpegNeedsDHT(const char* data, size_t* size, size_t* locSOF) {
return false;
}
wpi::StringRef JpegGetDHT() {
return wpi::StringRef(reinterpret_cast<const char*>(dhtData),
sizeof(dhtData));
std::string_view JpegGetDHT() {
return {reinterpret_cast<const char*>(dhtData), sizeof(dhtData)};
}
static inline void ReadInto(wpi::raw_istream& is, std::string& buf,

View File

@@ -6,8 +6,7 @@
#define CSCORE_JPEGUTIL_H_
#include <string>
#include <wpi/StringRef.h>
#include <string_view>
namespace wpi {
class raw_istream;
@@ -15,13 +14,13 @@ class raw_istream;
namespace cs {
bool IsJpeg(wpi::StringRef data);
bool IsJpeg(std::string_view data);
bool GetJpegSize(wpi::StringRef data, int* width, int* height);
bool GetJpegSize(std::string_view data, int* width, int* height);
bool JpegNeedsDHT(const char* data, size_t* size, size_t* locSOF);
wpi::StringRef JpegGetDHT();
std::string_view JpegGetDHT();
bool ReadJpeg(wpi::raw_istream& is, std::string& buf, int* width, int* height);

View File

@@ -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.
#include "Log.h"
void cs::NamedLogV(wpi::Logger& logger, unsigned int level, const char* file,
unsigned int line, std::string_view name,
fmt::string_view format, fmt::format_args args) {
fmt::memory_buffer out;
fmt::format_to(fmt::appender{out}, "{}: ", name);
fmt::vformat_to(fmt::appender{out}, format, args);
out.push_back('\0');
logger.DoLog(level, file, line, out.data());
}

View File

@@ -5,29 +5,71 @@
#ifndef CSCORE_LOG_H_
#define CSCORE_LOG_H_
#include <string_view>
#include <wpi/Logger.h>
#define LOG(level, x) WPI_LOG(m_logger, level, x)
namespace cs {
void NamedLogV(wpi::Logger& logger, unsigned int level, const char* file,
unsigned int line, std::string_view name,
fmt::string_view format, fmt::format_args args);
template <typename S, typename... Args>
inline void NamedLog(wpi::Logger& logger, unsigned int level, const char* file,
unsigned int line, std::string_view name, const S& format,
Args&&... args) {
if (logger.HasLogger() && level >= logger.min_level()) {
NamedLogV(logger, level, file, line, name, format,
fmt::make_args_checked<Args...>(format, args...));
}
}
} // namespace cs
#define LOG(level, format, ...) WPI_LOG(m_logger, level, format, __VA_ARGS__)
#undef ERROR
#define ERROR(x) WPI_ERROR(m_logger, x)
#define WARNING(x) WPI_WARNING(m_logger, x)
#define INFO(x) WPI_INFO(m_logger, x)
#define ERROR(format, ...) WPI_ERROR(m_logger, format, __VA_ARGS__)
#define WARNING(format, ...) WPI_WARNING(m_logger, format, __VA_ARGS__)
#define INFO(format, ...) WPI_INFO(m_logger, format, __VA_ARGS__)
#define DEBUG0(x) WPI_DEBUG(m_logger, x)
#define DEBUG1(x) WPI_DEBUG1(m_logger, x)
#define DEBUG2(x) WPI_DEBUG2(m_logger, x)
#define DEBUG3(x) WPI_DEBUG3(m_logger, x)
#define DEBUG4(x) WPI_DEBUG4(m_logger, x)
#define DEBUG0(format, ...) WPI_DEBUG(m_logger, format, __VA_ARGS__)
#define DEBUG1(format, ...) WPI_DEBUG1(m_logger, format, __VA_ARGS__)
#define DEBUG2(format, ...) WPI_DEBUG2(m_logger, format, __VA_ARGS__)
#define DEBUG3(format, ...) WPI_DEBUG3(m_logger, format, __VA_ARGS__)
#define DEBUG4(format, ...) WPI_DEBUG4(m_logger, format, __VA_ARGS__)
#define SERROR(x) ERROR(GetName() << ": " << x)
#define SWARNING(x) WARNING(GetName() << ": " << x)
#define SINFO(x) INFO(GetName() << ": " << x)
#define SLOG(level, format, ...) \
NamedLog(m_logger, level, __FILE__, __LINE__, GetName(), FMT_STRING(format), \
__VA_ARGS__)
#define SDEBUG(x) DEBUG0(GetName() << ": " << x)
#define SDEBUG1(x) DEBUG1(GetName() << ": " << x)
#define SDEBUG2(x) DEBUG2(GetName() << ": " << x)
#define SDEBUG3(x) DEBUG3(GetName() << ": " << x)
#define SDEBUG4(x) DEBUG4(GetName() << ": " << x)
#define SERROR(format, ...) SLOG(::wpi::WPI_LOG_ERROR, format, __VA_ARGS__)
#define SWARNING(format, ...) SLOG(::wpi::WPI_LOG_WARNING, format, __VA_ARGS__)
#define SINFO(format, ...) SLOG(::wpi::WPI_LOG_INFO, format, __VA_ARGS__)
#ifdef NDEBUG
#define SDEBUG(format, ...) \
do { \
} while (0)
#define SDEBUG1(format, ...) \
do { \
} while (0)
#define SDEBUG2(format, ...) \
do { \
} while (0)
#define SDEBUG3(format, ...) \
do { \
} while (0)
#define SDEBUG4(format, ...) \
do { \
} while (0)
#else
#define SDEBUG(format, ...) SLOG(::wpi::WPI_LOG_DEBUG, format, __VA_ARGS__)
#define SDEBUG1(format, ...) SLOG(::wpi::WPI_LOG_DEBUG1, format, __VA_ARGS__)
#define SDEBUG2(format, ...) SLOG(::wpi::WPI_LOG_DEBUG2, format, __VA_ARGS__)
#define SDEBUG3(format, ...) SLOG(::wpi::WPI_LOG_DEBUG3, format, __VA_ARGS__)
#define SDEBUG4(format, ...) SLOG(::wpi::WPI_LOG_DEBUG4, format, __VA_ARGS__)
#endif
#endif // CSCORE_LOG_H_

View File

@@ -6,9 +6,12 @@
#include <chrono>
#include <fmt/format.h>
#include <wpi/HttpUtil.h>
#include <wpi/SmallString.h>
#include <wpi/StringExtras.h>
#include <wpi/TCPAcceptor.h>
#include <wpi/fmt/raw_ostream.h>
#include <wpi/raw_socket_istream.h>
#include <wpi/raw_socket_ostream.h>
@@ -72,13 +75,13 @@ static const char* endRootPage = "</div></body></html>";
class MjpegServerImpl::ConnThread : public wpi::SafeThread {
public:
explicit ConnThread(const wpi::Twine& name, wpi::Logger& logger)
: m_name(name.str()), m_logger(logger) {}
explicit ConnThread(std::string_view name, wpi::Logger& logger)
: m_name(name), m_logger(logger) {}
void Main() override;
bool ProcessCommand(wpi::raw_ostream& os, SourceImpl& source,
wpi::StringRef parameters, bool respond);
std::string_view parameters, bool respond);
void SendJSON(wpi::raw_ostream& os, SourceImpl& source, bool header);
void SendHTMLHeadTitle(wpi::raw_ostream& os) const;
void SendHTML(wpi::raw_ostream& os, SourceImpl& source, bool header);
@@ -99,7 +102,7 @@ class MjpegServerImpl::ConnThread : public wpi::SafeThread {
std::string m_name;
wpi::Logger& m_logger;
wpi::StringRef GetName() { return m_name; }
std::string_view GetName() { return m_name; }
std::shared_ptr<SourceImpl> GetSource() {
std::scoped_lock lock(m_mutex);
@@ -130,10 +133,9 @@ class MjpegServerImpl::ConnThread : public wpi::SafeThread {
// Using cached pictures would lead to showing old/outdated pictures.
// Many browsers seem to ignore, or at least not always obey, those headers.
static void SendHeader(wpi::raw_ostream& os, int code,
const wpi::Twine& codeText,
const wpi::Twine& contentType,
const wpi::Twine& extra = wpi::Twine{}) {
os << "HTTP/1.0 " << code << ' ' << codeText << "\r\n";
std::string_view codeText, std::string_view contentType,
std::string_view extra = {}) {
fmt::print(os, "HTTP/1.0 {} {}\r\n", code, codeText);
os << "Connection: close\r\n"
"Server: CameraServer/1.0\r\n"
"Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, "
@@ -142,10 +144,8 @@ static void SendHeader(wpi::raw_ostream& os, int code,
"Expires: Mon, 3 Jan 2000 12:34:56 GMT\r\n";
os << "Content-Type: " << contentType << "\r\n";
os << "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: *\r\n";
wpi::SmallString<128> extraBuf;
wpi::StringRef extraStr = extra.toStringRef(extraBuf);
if (!extraStr.empty()) {
os << extraStr << "\r\n";
if (!extra.empty()) {
os << extra << "\r\n";
}
os << "\r\n"; // header ends with a blank line
}
@@ -154,8 +154,8 @@ static void SendHeader(wpi::raw_ostream& os, int code,
// @param code HTTP error code (e.g. 404)
// @param message Additional message text
static void SendError(wpi::raw_ostream& os, int code,
const wpi::Twine& message) {
wpi::StringRef codeText, extra, baseMessage;
std::string_view message) {
std::string_view codeText, extra, baseMessage;
switch (code) {
case 401:
codeText = "Unauthorized";
@@ -195,66 +195,61 @@ static void SendError(wpi::raw_ostream& os, int code,
// Perform a command specified by HTTP GET parameters.
bool MjpegServerImpl::ConnThread::ProcessCommand(wpi::raw_ostream& os,
SourceImpl& source,
wpi::StringRef parameters,
std::string_view parameters,
bool respond) {
wpi::SmallString<256> responseBuf;
wpi::raw_svector_ostream response{responseBuf};
// command format: param1=value1&param2=value2...
while (!parameters.empty()) {
// split out next param and value
wpi::StringRef rawParam, rawValue;
std::tie(rawParam, parameters) = parameters.split('&');
std::string_view rawParam, rawValue;
std::tie(rawParam, parameters) = wpi::split(parameters, '&');
if (rawParam.empty()) {
continue; // ignore "&&"
}
std::tie(rawParam, rawValue) = rawParam.split('=');
std::tie(rawParam, rawValue) = wpi::split(rawParam, '=');
if (rawParam.empty() || rawValue.empty()) {
continue; // ignore "param="
}
SDEBUG4("HTTP parameter \"" << rawParam << "\" value \"" << rawValue
<< "\"");
SDEBUG4("HTTP parameter \"{}\" value \"{}\"", rawParam, rawValue);
// unescape param
bool error = false;
wpi::SmallString<64> paramBuf;
wpi::StringRef param = wpi::UnescapeURI(rawParam, paramBuf, &error);
std::string_view param = wpi::UnescapeURI(rawParam, paramBuf, &error);
if (error) {
wpi::SmallString<128> error;
wpi::raw_svector_ostream oss{error};
oss << "could not unescape parameter \"" << rawParam << "\"";
SendError(os, 500, error.str());
SDEBUG(error.str());
auto estr = fmt::format("could not unescape parameter \"{}\"", rawParam);
SendError(os, 500, estr);
SDEBUG("{}", estr);
return false;
}
// unescape value
wpi::SmallString<64> valueBuf;
wpi::StringRef value = wpi::UnescapeURI(rawValue, valueBuf, &error);
std::string_view value = wpi::UnescapeURI(rawValue, valueBuf, &error);
if (error) {
wpi::SmallString<128> error;
wpi::raw_svector_ostream oss{error};
oss << "could not unescape value \"" << rawValue << "\"";
SendError(os, 500, error.str());
SDEBUG(error.str());
auto estr = fmt::format("could not unescape value \"{}\"", rawValue);
SendError(os, 500, estr);
SDEBUG("{}", estr);
return false;
}
// Handle resolution, compression, and FPS. These are handled locally
// rather than passed to the source.
if (param == "resolution") {
wpi::StringRef widthStr, heightStr;
std::tie(widthStr, heightStr) = value.split('x');
int width, height;
if (widthStr.getAsInteger(10, width)) {
auto [widthStr, heightStr] = wpi::split(value, 'x');
int width = wpi::parse_integer<int>(widthStr, 10).value_or(-1);
int height = wpi::parse_integer<int>(heightStr, 10).value_or(-1);
if (width < 0) {
response << param << ": \"width is not an integer\"\r\n";
SWARNING("HTTP parameter \"" << param << "\" width \"" << widthStr
<< "\" is not an integer");
SWARNING("HTTP parameter \"{}\" width \"{}\" is not an integer", param,
widthStr);
continue;
}
if (heightStr.getAsInteger(10, height)) {
if (height < 0) {
response << param << ": \"height is not an integer\"\r\n";
SWARNING("HTTP parameter \"" << param << "\" height \"" << heightStr
<< "\" is not an integer");
SWARNING("HTTP parameter \"{}\" height \"{}\" is not an integer", param,
heightStr);
continue;
}
m_width = width;
@@ -264,29 +259,25 @@ bool MjpegServerImpl::ConnThread::ProcessCommand(wpi::raw_ostream& os,
}
if (param == "fps") {
int fps;
if (value.getAsInteger(10, fps)) {
response << param << ": \"invalid integer\"\r\n";
SWARNING("HTTP parameter \"" << param << "\" value \"" << value
<< "\" is not an integer");
continue;
} else {
m_fps = fps;
if (auto v = wpi::parse_integer<int>(value, 10)) {
m_fps = v.value();
response << param << ": \"ok\"\r\n";
} else {
response << param << ": \"invalid integer\"\r\n";
SWARNING("HTTP parameter \"{}\" value \"{}\" is not an integer", param,
value);
}
continue;
}
if (param == "compression") {
int compression;
if (value.getAsInteger(10, compression)) {
response << param << ": \"invalid integer\"\r\n";
SWARNING("HTTP parameter \"" << param << "\" value \"" << value
<< "\" is not an integer");
continue;
} else {
m_compression = compression;
if (auto v = wpi::parse_integer<int>(value, 10)) {
m_compression = v.value();
response << param << ": \"ok\"\r\n";
} else {
response << param << ": \"invalid integer\"\r\n";
SWARNING("HTTP parameter \"{}\" value \"{}\" is not an integer", param,
value);
}
continue;
}
@@ -300,7 +291,7 @@ bool MjpegServerImpl::ConnThread::ProcessCommand(wpi::raw_ostream& os,
auto prop = source.GetPropertyIndex(param);
if (!prop) {
response << param << ": \"ignored\"\r\n";
SWARNING("ignoring HTTP parameter \"" << param << "\"");
SWARNING("ignoring HTTP parameter \"{}\"", param);
continue;
}
@@ -310,21 +301,20 @@ bool MjpegServerImpl::ConnThread::ProcessCommand(wpi::raw_ostream& os,
case CS_PROP_BOOLEAN:
case CS_PROP_INTEGER:
case CS_PROP_ENUM: {
int val = 0;
if (value.getAsInteger(10, val)) {
response << param << ": \"invalid integer\"\r\n";
SWARNING("HTTP parameter \"" << param << "\" value \"" << value
<< "\" is not an integer");
if (auto v = wpi::parse_integer<int>(value, 10)) {
fmt::print(response, "{}: {}\r\n", param, v.value());
SDEBUG4("HTTP parameter \"{}\" value {}", param, value);
source.SetProperty(prop, v.value(), &status);
} else {
response << param << ": " << val << "\r\n";
SDEBUG4("HTTP parameter \"" << param << "\" value " << value);
source.SetProperty(prop, val, &status);
response << param << ": \"invalid integer\"\r\n";
SWARNING("HTTP parameter \"{}\" value \"{}\" is not an integer",
param, value);
}
break;
}
case CS_PROP_STRING: {
response << param << ": \"ok\"\r\n";
SDEBUG4("HTTP parameter \"" << param << "\" value \"" << value << "\"");
SDEBUG4("HTTP parameter \"{}\" value \"{}\"", param, value);
source.SetStringProperty(prop, value, &status);
break;
}
@@ -362,17 +352,17 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
for (auto prop : source.EnumerateProperties(properties_vec, &status)) {
wpi::SmallString<128> name_buf;
auto name = source.GetPropertyName(prop, name_buf, &status);
if (name.startswith("raw_")) {
if (wpi::starts_with(name, "raw_")) {
continue;
}
auto kind = source.GetPropertyKind(prop);
os << "<p />"
<< "<label for=\"" << name << "\">" << name << "</label>\n";
fmt::print(os, "<p /><label for=\"{0}\">{0}</label>\n", name);
switch (kind) {
case CS_PROP_BOOLEAN:
os << "<input id=\"" << name
<< "\" type=\"checkbox\" onclick=\"update('" << name
<< "', this.checked ? 1 : 0)\" ";
fmt::print(os,
"<input id=\"{0}\" type=\"checkbox\" "
"onclick=\"update('{0}', this.checked ? 1 : 0)\" ",
name);
if (source.GetProperty(prop, &status) != 0) {
os << "checked />\n";
} else {
@@ -384,12 +374,13 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
auto min = source.GetPropertyMin(prop, &status);
auto max = source.GetPropertyMax(prop, &status);
auto step = source.GetPropertyStep(prop, &status);
os << "<input type=\"range\" min=\"" << min << "\" max=\"" << max
<< "\" value=\"" << valI << "\" id=\"" << name << "\" step=\""
<< step << "\" oninput=\"updateInt('#" << name << "op', '" << name
<< "', value)\" />\n";
os << "<output for=\"" << name << "\" id=\"" << name << "op\">" << valI
<< "</output>\n";
fmt::print(os,
"<input type=\"range\" min=\"{1}\" max=\"{2}\" "
"value=\"{3}\" id=\"{0}\" step=\"{4}\" "
"oninput=\"updateInt('#{0}op', '{0}', value)\" />\n",
name, min, max, valI, step);
fmt::print(os, "<output for=\"{0}\" id=\"{0}op\">{1}</output>\n", name,
valI);
break;
}
case CS_PROP_ENUM: {
@@ -404,26 +395,30 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
// replace any non-printable characters in name with spaces
wpi::SmallString<128> ch_name;
for (char ch : *choice) {
ch_name.push_back(std::isprint(ch) ? ch : ' ');
ch_name.push_back(wpi::isPrint(ch) ? ch : ' ');
}
os << "<input id=\"" << name << j << "\" type=\"radio\" name=\""
<< name << "\" value=\"" << ch_name << "\" onclick=\"update('"
<< name << "', " << j << ")\"";
fmt::print(os,
"<input id=\"{0}{1}\" type=\"radio\" name=\"{0}\" "
"value=\"{2}\" onclick=\"update('{0}', {1})\"",
name, j, ch_name);
if (j == valE) {
os << " checked";
}
os << " /><label for=\"" << name << j << "\">" << ch_name
<< "</label>\n";
fmt::print(os, " /><label for=\"{}{}\">{}</label>\n", name, j,
ch_name);
}
break;
}
case CS_PROP_STRING: {
wpi::SmallString<128> strval_buf;
os << "<input type=\"text\" id=\"" << name << "box\" name=\"" << name
<< "\" value=\""
<< source.GetStringProperty(prop, strval_buf, &status) << "\" />\n";
os << "<input type=\"button\" value =\"Submit\" onclick=\"update('"
<< name << "', " << name << "box.value)\" />\n";
fmt::print(os,
"<input type=\"text\" id=\"{0}box\" name=\"{0}\" "
"value=\"{1}\" />\n",
name, source.GetStringProperty(prop, strval_buf, &status));
fmt::print(os,
"<input type=\"button\" value =\"Submit\" "
"onclick=\"update('{0}', {0}box.value)\" />\n",
name);
break;
}
default:
@@ -469,10 +464,8 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
os << "unknown";
break;
}
os << "</td><td>" << mode.width;
os << "</td><td>" << mode.height;
os << "</td><td>" << mode.fps;
os << "</td></tr>";
fmt::print(os, "</td><td>{}</td><td>{}</td><td>{}</td></tr>", mode.width,
mode.height, mode.fps);
}
os << "</table>\n";
os << endRootPage << "\r\n";
@@ -500,20 +493,21 @@ void MjpegServerImpl::ConnThread::SendJSON(wpi::raw_ostream& os,
wpi::SmallString<128> name_buf;
auto name = source.GetPropertyName(prop, name_buf, &status);
auto kind = source.GetPropertyKind(prop);
os << "\n\"name\": \"" << name << '"';
os << ",\n\"id\": \"" << prop << '"';
os << ",\n\"type\": \"" << kind << '"';
os << ",\n\"min\": \"" << source.GetPropertyMin(prop, &status) << '"';
os << ",\n\"max\": \"" << source.GetPropertyMax(prop, &status) << '"';
os << ",\n\"step\": \"" << source.GetPropertyStep(prop, &status) << '"';
os << ",\n\"default\": \"" << source.GetPropertyDefault(prop, &status)
<< '"';
fmt::print(os, "\n\"name\": \"{}\"", name);
fmt::print(os, ",\n\"id\": \"{}\"", prop);
fmt::print(os, ",\n\"type\": \"{}\"", kind);
fmt::print(os, ",\n\"min\": \"{}\"", source.GetPropertyMin(prop, &status));
fmt::print(os, ",\n\"max\": \"{}\"", source.GetPropertyMax(prop, &status));
fmt::print(os, ",\n\"step\": \"{}\"",
source.GetPropertyStep(prop, &status));
fmt::print(os, ",\n\"default\": \"{}\"",
source.GetPropertyDefault(prop, &status));
os << ",\n\"value\": \"";
switch (kind) {
case CS_PROP_BOOLEAN:
case CS_PROP_INTEGER:
case CS_PROP_ENUM:
os << source.GetProperty(prop, &status);
fmt::print(os, "{}", source.GetProperty(prop, &status));
break;
case CS_PROP_STRING: {
wpi::SmallString<128> strval_buf;
@@ -543,7 +537,7 @@ void MjpegServerImpl::ConnThread::SendJSON(wpi::raw_ostream& os,
for (char ch : *choice) {
ch_name.push_back(std::isprint(ch) ? ch : ' ');
}
os << '"' << j << "\": \"" << ch_name << '"';
fmt::print(os, "\"{}\": \"{}\"", j, ch_name);
}
os << "}\n";
}
@@ -579,29 +573,26 @@ void MjpegServerImpl::ConnThread::SendJSON(wpi::raw_ostream& os,
os << "unknown";
break;
}
os << "\",\n\"width\": \"" << mode.width << '"';
os << ",\n\"height\": \"" << mode.height << '"';
os << ",\n\"fps\": \"" << mode.fps << '"';
fmt::print(os, "\",\n\"width\": \"{}\"", mode.width);
fmt::print(os, ",\n\"height\": \"{}\"", mode.height);
fmt::print(os, ",\n\"fps\": \"{}\"", mode.fps);
os << '}';
}
os << "\n]\n}\n";
os.flush();
}
MjpegServerImpl::MjpegServerImpl(const wpi::Twine& name, wpi::Logger& logger,
MjpegServerImpl::MjpegServerImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry,
const wpi::Twine& listenAddress, int port,
std::string_view listenAddress, int port,
std::unique_ptr<wpi::NetworkAcceptor> acceptor)
: SinkImpl{name, logger, notifier, telemetry},
m_listenAddress(listenAddress.str()),
m_listenAddress(listenAddress),
m_port(port),
m_acceptor{std::move(acceptor)} {
m_active = true;
wpi::SmallString<128> descBuf;
wpi::raw_svector_ostream desc{descBuf};
desc << "HTTP Server on port " << port;
SetDescription(desc.str());
SetDescription(fmt::format("HTTP Server on port {}", port));
// Create properties
m_widthProp = CreateProperty("width", [] {
@@ -659,7 +650,7 @@ void MjpegServerImpl::Stop() {
// Send HTTP response and a stream of JPG-frames
void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) {
if (m_noStreaming) {
SERROR("Too many simultaneous client streams");
SERROR("{}", "Too many simultaneous client streams");
SendError(os, 503, "Too many simultaneous streams");
return;
}
@@ -672,7 +663,7 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) {
SendHeader(oss, 200, "OK", "multipart/x-mixed-replace;boundary=" BOUNDARY);
os << oss.str();
SDEBUG("Headers send, sending stream now");
SDEBUG("{}", "Headers send, sending stream now");
Frame::Time lastFrameTime = 0;
Frame::Time timePerFrame = 0;
@@ -694,7 +685,7 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
continue;
}
SDEBUG4("waiting for frame");
SDEBUG4("{}", "waiting for frame");
Frame frame = source->GetNextFrame(0.225); // blocks
if (!m_active) {
break;
@@ -757,7 +748,7 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) {
continue;
}
SDEBUG4("sending frame size=" << size << " addDHT=" << addDHT);
SDEBUG4("sending frame size={} addDHT={}", size, addDHT);
// print the individual mimetype and the length
// sending the content-length fixes random stream disruption observed
@@ -766,18 +757,18 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) {
double timestamp = lastFrameTime / 1000000.0;
header.clear();
oss << "\r\n--" BOUNDARY "\r\n"
<< "Content-Type: image/jpeg\r\n"
<< "Content-Length: " << size << "\r\n"
<< "X-Timestamp: " << timestamp << "\r\n"
<< "\r\n";
<< "Content-Type: image/jpeg\r\n";
fmt::print(oss, "Content-Length: {}\r\n", size);
fmt::print(oss, "X-Timestamp: {}\r\n", timestamp);
oss << "\r\n";
os << oss.str();
if (addDHT) {
// Insert DHT data immediately before SOF
os << wpi::StringRef(data, locSOF);
os << std::string_view(data, locSOF);
os << JpegGetDHT();
os << wpi::StringRef(data + locSOF, image->size() - locSOF);
os << std::string_view(data + locSOF, image->size() - locSOF);
} else {
os << wpi::StringRef(data, size);
os << std::string_view(data, size);
}
// os.flush();
}
@@ -790,48 +781,50 @@ void MjpegServerImpl::ConnThread::ProcessRequest() {
// Read the request string from the stream
wpi::SmallString<128> reqBuf;
wpi::StringRef req = is.getline(reqBuf, 4096);
std::string_view req = is.getline(reqBuf, 4096);
if (is.has_error()) {
SDEBUG("error getting request string");
SDEBUG("{}", "error getting request string");
return;
}
enum { kCommand, kStream, kGetSettings, kGetSourceConfig, kRootPage } kind;
wpi::StringRef parameters;
std::string_view parameters;
size_t pos;
SDEBUG("HTTP request: '" << req << "'\n");
SDEBUG("HTTP request: '{}'\n", req);
// Determine request kind. Most of these are for mjpgstreamer
// compatibility, others are for Axis camera compatibility.
if ((pos = req.find("POST /stream")) != wpi::StringRef::npos) {
if ((pos = req.find("POST /stream")) != std::string_view::npos) {
kind = kStream;
parameters = req.substr(req.find('?', pos + 12)).substr(1);
} else if ((pos = req.find("GET /?action=stream")) != wpi::StringRef::npos) {
} else if ((pos = req.find("GET /?action=stream")) !=
std::string_view::npos) {
kind = kStream;
parameters = req.substr(req.find('&', pos + 19)).substr(1);
} else if ((pos = req.find("GET /stream.mjpg")) != wpi::StringRef::npos) {
} else if ((pos = req.find("GET /stream.mjpg")) != std::string_view::npos) {
kind = kStream;
parameters = req.substr(req.find('?', pos + 16)).substr(1);
} else if (req.find("GET /settings") != wpi::StringRef::npos &&
req.find(".json") != wpi::StringRef::npos) {
} else if (req.find("GET /settings") != std::string_view::npos &&
req.find(".json") != std::string_view::npos) {
kind = kGetSettings;
} else if (req.find("GET /config") != wpi::StringRef::npos &&
req.find(".json") != wpi::StringRef::npos) {
} else if (req.find("GET /config") != std::string_view::npos &&
req.find(".json") != std::string_view::npos) {
kind = kGetSourceConfig;
} else if (req.find("GET /input") != wpi::StringRef::npos &&
req.find(".json") != wpi::StringRef::npos) {
} else if (req.find("GET /input") != std::string_view::npos &&
req.find(".json") != std::string_view::npos) {
kind = kGetSettings;
} else if (req.find("GET /output") != wpi::StringRef::npos &&
req.find(".json") != wpi::StringRef::npos) {
} else if (req.find("GET /output") != std::string_view::npos &&
req.find(".json") != std::string_view::npos) {
kind = kGetSettings;
} else if ((pos = req.find("GET /?action=command")) != wpi::StringRef::npos) {
} else if ((pos = req.find("GET /?action=command")) !=
std::string_view::npos) {
kind = kCommand;
parameters = req.substr(req.find('&', pos + 20)).substr(1);
} else if (req.find("GET / ") != wpi::StringRef::npos || req == "GET /\n") {
} else if (req.find("GET / ") != std::string_view::npos || req == "GET /\n") {
kind = kRootPage;
} else {
SDEBUG("HTTP request resource not found");
SDEBUG("{}", "HTTP request resource not found");
SendError(os, 404, "Resource not found");
return;
}
@@ -841,13 +834,13 @@ void MjpegServerImpl::ConnThread::ProcessRequest() {
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
"-=&1234567890%./");
parameters = parameters.substr(0, pos);
SDEBUG("command parameters: \"" << parameters << "\"");
SDEBUG("command parameters: \"{}\"", parameters);
// Read the rest of the HTTP request.
// The end of the request is marked by a single, empty line
wpi::SmallString<128> lineBuf;
for (;;) {
if (is.getline(lineBuf, 4096).startswith("\n")) {
if (wpi::starts_with(is.getline(lineBuf, 4096), "\n")) {
break;
}
if (is.has_error()) {
@@ -859,7 +852,7 @@ void MjpegServerImpl::ConnThread::ProcessRequest() {
switch (kind) {
case kStream:
if (auto source = GetSource()) {
SDEBUG("request for stream " << source->GetName());
SDEBUG("request for stream {}", source->GetName());
if (!ProcessCommand(os, *source, parameters, false)) {
return;
}
@@ -873,11 +866,11 @@ void MjpegServerImpl::ConnThread::ProcessRequest() {
SendHeader(os, 200, "OK", "text/plain");
os << "Ignored due to no connected source."
<< "\r\n";
SDEBUG("Ignored due to no connected source.");
SDEBUG("{}", "Ignored due to no connected source.");
}
break;
case kGetSettings:
SDEBUG("request for JSON file");
SDEBUG("{}", "request for JSON file");
if (auto source = GetSource()) {
SendJSON(os, *source, true);
} else {
@@ -885,7 +878,7 @@ void MjpegServerImpl::ConnThread::ProcessRequest() {
}
break;
case kGetSourceConfig:
SDEBUG("request for JSON file");
SDEBUG("{}", "request for JSON file");
if (auto source = GetSource()) {
SendHeader(os, 200, "OK", "application/json");
CS_Status status = CS_OK;
@@ -896,7 +889,7 @@ void MjpegServerImpl::ConnThread::ProcessRequest() {
}
break;
case kRootPage:
SDEBUG("request for root page");
SDEBUG("{}", "request for root page");
SendHeader(os, 200, "OK", "text/html");
if (auto source = GetSource()) {
SendHTML(os, *source, false);
@@ -907,7 +900,7 @@ void MjpegServerImpl::ConnThread::ProcessRequest() {
break;
}
SDEBUG("leaving HTTP client thread");
SDEBUG("{}", "leaving HTTP client thread");
}
// worker thread for clients that connected to this server
@@ -934,7 +927,7 @@ void MjpegServerImpl::ServerThreadMain() {
return;
}
SDEBUG("waiting for clients to connect");
SDEBUG("{}", "waiting for clients to connect");
while (m_active) {
auto stream = m_acceptor->accept();
if (!stream) {
@@ -945,7 +938,7 @@ void MjpegServerImpl::ServerThreadMain() {
return;
}
SDEBUG("client connection from " << stream->getPeerIP());
SDEBUG("client connection from {}", stream->getPeerIP());
auto source = GetSource();
@@ -984,7 +977,7 @@ void MjpegServerImpl::ServerThreadMain() {
thr->m_cond.notify_one();
}
SDEBUG("leaving server thread");
SDEBUG("{}", "leaving server thread");
}
void MjpegServerImpl::SetSourceImpl(std::shared_ptr<SourceImpl> source) {
@@ -1007,19 +1000,15 @@ void MjpegServerImpl::SetSourceImpl(std::shared_ptr<SourceImpl> source) {
namespace cs {
CS_Sink CreateMjpegServer(const wpi::Twine& name,
const wpi::Twine& listenAddress, int port,
CS_Status* status) {
CS_Sink CreateMjpegServer(std::string_view name, std::string_view listenAddress,
int port, CS_Status* status) {
auto& inst = Instance::GetInstance();
wpi::SmallString<128> listenAddressBuf;
return inst.CreateSink(
CS_SINK_MJPEG,
std::make_shared<MjpegServerImpl>(
name, inst.logger, inst.notifier, inst.telemetry, listenAddress, port,
std::unique_ptr<wpi::NetworkAcceptor>(new wpi::TCPAcceptor(
port,
listenAddress.toNullTerminatedStringRef(listenAddressBuf).data(),
inst.logger))));
std::unique_ptr<wpi::NetworkAcceptor>(
new wpi::TCPAcceptor(port, listenAddress, inst.logger))));
}
std::string GetMjpegServerListenAddress(CS_Sink sink, CS_Status* status) {

View File

@@ -8,6 +8,7 @@
#include <atomic>
#include <memory>
#include <string>
#include <string_view>
#include <thread>
#include <vector>
@@ -15,7 +16,6 @@
#include <wpi/NetworkStream.h>
#include <wpi/SafeThread.h>
#include <wpi/SmallVector.h>
#include <wpi/Twine.h>
#include <wpi/raw_istream.h>
#include <wpi/raw_ostream.h>
#include <wpi/raw_socket_ostream.h>
@@ -28,9 +28,9 @@ class SourceImpl;
class MjpegServerImpl : public SinkImpl {
public:
MjpegServerImpl(const wpi::Twine& name, wpi::Logger& logger,
MjpegServerImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry,
const wpi::Twine& listenAddress, int port,
std::string_view listenAddress, int port,
std::unique_ptr<wpi::NetworkAcceptor> acceptor);
~MjpegServerImpl() override;

View File

@@ -32,7 +32,7 @@ unsigned int Notifier::AddPolled(unsigned int pollerUid, int eventMask) {
return DoAdd(pollerUid, eventMask);
}
void Notifier::NotifySource(const wpi::Twine& name, CS_Source source,
void Notifier::NotifySource(std::string_view name, CS_Source source,
CS_EventKind kind) {
Send(UINT_MAX, name, source, static_cast<RawEvent::Kind>(kind));
}
@@ -49,9 +49,9 @@ void Notifier::NotifySourceVideoMode(const SourceImpl& source,
}
void Notifier::NotifySourceProperty(const SourceImpl& source, CS_EventKind kind,
const wpi::Twine& propertyName,
int property, CS_PropertyKind propertyKind,
int value, const wpi::Twine& valueStr) {
std::string_view propertyName, int property,
CS_PropertyKind propertyKind, int value,
std::string_view valueStr) {
auto handleData = Instance::GetInstance().FindSource(source);
Send(UINT_MAX, propertyName, handleData.first,
static_cast<RawEvent::Kind>(kind),
@@ -59,7 +59,7 @@ void Notifier::NotifySourceProperty(const SourceImpl& source, CS_EventKind kind,
value, valueStr);
}
void Notifier::NotifySink(const wpi::Twine& name, CS_Sink sink,
void Notifier::NotifySink(std::string_view name, CS_Sink sink,
CS_EventKind kind) {
Send(UINT_MAX, name, sink, static_cast<RawEvent::Kind>(kind));
}
@@ -69,7 +69,7 @@ void Notifier::NotifySink(const SinkImpl& sink, CS_EventKind kind) {
NotifySink(sink.GetName(), handleData.first, kind);
}
void Notifier::NotifySinkSourceChanged(const wpi::Twine& name, CS_Sink sink,
void Notifier::NotifySinkSourceChanged(std::string_view name, CS_Sink sink,
CS_Source source) {
RawEvent event{name, sink, RawEvent::kSinkSourceChanged};
event.sourceHandle = source;
@@ -77,9 +77,9 @@ void Notifier::NotifySinkSourceChanged(const wpi::Twine& name, CS_Sink sink,
}
void Notifier::NotifySinkProperty(const SinkImpl& sink, CS_EventKind kind,
const wpi::Twine& propertyName, int property,
std::string_view propertyName, int property,
CS_PropertyKind propertyKind, int value,
const wpi::Twine& valueStr) {
std::string_view valueStr) {
auto handleData = Instance::GetInstance().FindSink(sink);
Send(UINT_MAX, propertyName, handleData.first,
static_cast<RawEvent::Kind>(kind),

View File

@@ -65,22 +65,21 @@ class Notifier : public wpi::CallbackManager<Notifier, impl::NotifierThread> {
unsigned int AddPolled(unsigned int pollerUid, int eventMask);
// Notification events
void NotifySource(const wpi::Twine& name, CS_Source source,
CS_EventKind kind);
void NotifySource(std::string_view name, CS_Source source, CS_EventKind kind);
void NotifySource(const SourceImpl& source, CS_EventKind kind);
void NotifySourceVideoMode(const SourceImpl& source, const VideoMode& mode);
void NotifySourceProperty(const SourceImpl& source, CS_EventKind kind,
const wpi::Twine& propertyName, int property,
std::string_view propertyName, int property,
CS_PropertyKind propertyKind, int value,
const wpi::Twine& valueStr);
void NotifySink(const wpi::Twine& name, CS_Sink sink, CS_EventKind kind);
std::string_view valueStr);
void NotifySink(std::string_view name, CS_Sink sink, CS_EventKind kind);
void NotifySink(const SinkImpl& sink, CS_EventKind kind);
void NotifySinkSourceChanged(const wpi::Twine& name, CS_Sink sink,
void NotifySinkSourceChanged(std::string_view name, CS_Sink sink,
CS_Source source);
void NotifySinkProperty(const SinkImpl& sink, CS_EventKind kind,
const wpi::Twine& propertyName, int property,
std::string_view propertyName, int property,
CS_PropertyKind propertyKind, int value,
const wpi::Twine& valueStr);
std::string_view valueStr);
void NotifyNetworkInterfacesChanged();
void NotifyTelemetryUpdated();
void NotifyUsbCamerasChanged();

View File

@@ -11,15 +11,14 @@
using namespace cs;
int PropertyContainer::GetPropertyIndex(const wpi::Twine& name) const {
int PropertyContainer::GetPropertyIndex(std::string_view name) const {
// We can't fail, so instead we create a new index if caching fails.
CS_Status status = 0;
if (!m_properties_cached) {
CacheProperties(&status);
}
std::scoped_lock lock(m_mutex);
wpi::SmallVector<char, 64> nameBuf;
int& ndx = m_properties[name.toStringRef(nameBuf)];
int& ndx = m_properties[name];
if (ndx == 0) {
// create a new index
ndx = m_propertyData.size() + 1;
@@ -28,7 +27,7 @@ int PropertyContainer::GetPropertyIndex(const wpi::Twine& name) const {
return ndx;
}
wpi::ArrayRef<int> PropertyContainer::EnumerateProperties(
wpi::span<int> PropertyContainer::EnumerateProperties(
wpi::SmallVectorImpl<int>& vec, CS_Status* status) const {
if (!m_properties_cached && !CacheProperties(status)) {
return {};
@@ -55,7 +54,7 @@ CS_PropertyKind PropertyContainer::GetPropertyKind(int property) const {
return prop->propKind;
}
wpi::StringRef PropertyContainer::GetPropertyName(
std::string_view PropertyContainer::GetPropertyName(
int property, wpi::SmallVectorImpl<char>& buf, CS_Status* status) const {
if (!m_properties_cached && !CacheProperties(status)) {
return {};
@@ -108,7 +107,7 @@ void PropertyContainer::SetProperty(int property, int value,
return;
}
UpdatePropertyValue(property, false, value, wpi::Twine{});
UpdatePropertyValue(property, false, value, {});
}
int PropertyContainer::GetPropertyMin(int property, CS_Status* status) const {
@@ -164,7 +163,7 @@ int PropertyContainer::GetPropertyDefault(int property,
return prop->defaultValue;
}
wpi::StringRef PropertyContainer::GetStringProperty(
std::string_view PropertyContainer::GetStringProperty(
int property, wpi::SmallVectorImpl<char>& buf, CS_Status* status) const {
if (!m_properties_cached && !CacheProperties(status)) {
return {};
@@ -181,10 +180,10 @@ wpi::StringRef PropertyContainer::GetStringProperty(
}
buf.clear();
buf.append(prop->valueStr.begin(), prop->valueStr.end());
return wpi::StringRef(buf.data(), buf.size());
return {buf.data(), buf.size()};
}
void PropertyContainer::SetStringProperty(int property, const wpi::Twine& value,
void PropertyContainer::SetStringProperty(int property, std::string_view value,
CS_Status* status) {
std::scoped_lock lock(m_mutex);
auto prop = GetProperty(property);
@@ -225,7 +224,7 @@ std::vector<std::string> PropertyContainer::GetEnumPropertyChoices(
}
std::unique_ptr<PropertyImpl> PropertyContainer::CreateEmptyProperty(
const wpi::Twine& name) const {
std::string_view name) const {
return std::make_unique<PropertyImpl>(name);
}
@@ -237,16 +236,15 @@ bool PropertyContainer::CacheProperties(CS_Status* status) const {
bool PropertyContainer::SetPropertiesJson(const wpi::json& config,
wpi::Logger& logger,
wpi::StringRef logName,
std::string_view logName,
CS_Status* status) {
for (auto&& prop : config) {
std::string name;
try {
name = prop.at("name").get<std::string>();
} catch (const wpi::json::exception& e) {
WPI_WARNING(logger,
logName << ": SetConfigJson: could not read property name: "
<< e.what());
WPI_WARNING(logger, "{}: SetConfigJson: could not read property name: {}",
logName, e.what());
continue;
}
int n = GetPropertyIndex(name);
@@ -254,24 +252,24 @@ bool PropertyContainer::SetPropertiesJson(const wpi::json& config,
auto& v = prop.at("value");
if (v.is_string()) {
std::string val = v.get<std::string>();
WPI_INFO(logger, logName << ": SetConfigJson: setting property '"
<< name << "' to '" << val << '\'');
WPI_INFO(logger, "{}: SetConfigJson: setting property '{}' to '{}'",
logName, name, val);
SetStringProperty(n, val, status);
} else if (v.is_boolean()) {
bool val = v.get<bool>();
WPI_INFO(logger, logName << ": SetConfigJson: setting property '"
<< name << "' to " << val);
WPI_INFO(logger, "{}: SetConfigJson: setting property '{}' to {}",
logName, name, val);
SetProperty(n, val, status);
} else {
int val = v.get<int>();
WPI_INFO(logger, logName << ": SetConfigJson: setting property '"
<< name << "' to " << val);
WPI_INFO(logger, "{}: SetConfigJson: setting property '{}' to {}",
logName, name, val);
SetProperty(n, val, status);
}
} catch (const wpi::json::exception& e) {
WPI_WARNING(logger,
logName << ": SetConfigJson: could not read property value: "
<< e.what());
"{}: SetConfigJson: could not read property value: {}",
logName, e.what());
continue;
}
}

View File

@@ -9,20 +9,20 @@
#include <cstddef>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include <wpi/ArrayRef.h>
#include <wpi/SmallVector.h>
#include <wpi/StringMap.h>
#include <wpi/StringRef.h>
#include <wpi/Twine.h>
#include <wpi/mutex.h>
#include <wpi/span.h>
#include "PropertyImpl.h"
#include "cscore_cpp.h"
namespace wpi {
class Logger;
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
@@ -32,28 +32,29 @@ class PropertyContainer {
public:
virtual ~PropertyContainer() = default;
int GetPropertyIndex(const wpi::Twine& name) const;
wpi::ArrayRef<int> EnumerateProperties(wpi::SmallVectorImpl<int>& vec,
CS_Status* status) const;
int GetPropertyIndex(std::string_view name) const;
wpi::span<int> EnumerateProperties(wpi::SmallVectorImpl<int>& vec,
CS_Status* status) const;
CS_PropertyKind GetPropertyKind(int property) const;
wpi::StringRef GetPropertyName(int property, wpi::SmallVectorImpl<char>& buf,
CS_Status* status) const;
std::string_view GetPropertyName(int property,
wpi::SmallVectorImpl<char>& buf,
CS_Status* status) const;
int GetProperty(int property, CS_Status* status) const;
virtual void SetProperty(int property, int value, CS_Status* status);
int GetPropertyMin(int property, CS_Status* status) const;
int GetPropertyMax(int property, CS_Status* status) const;
int GetPropertyStep(int property, CS_Status* status) const;
int GetPropertyDefault(int property, CS_Status* status) const;
wpi::StringRef GetStringProperty(int property,
wpi::SmallVectorImpl<char>& buf,
CS_Status* status) const;
virtual void SetStringProperty(int property, const wpi::Twine& value,
std::string_view GetStringProperty(int property,
wpi::SmallVectorImpl<char>& buf,
CS_Status* status) const;
virtual void SetStringProperty(int property, std::string_view value,
CS_Status* status);
std::vector<std::string> GetEnumPropertyChoices(int property,
CS_Status* status) const;
bool SetPropertiesJson(const wpi::json& config, wpi::Logger& logger,
wpi::StringRef logName, CS_Status* status);
std::string_view logName, CS_Status* status);
wpi::json GetPropertiesJsonObject(CS_Status* status);
protected:
@@ -76,10 +77,9 @@ class PropertyContainer {
// @tparam NewFunc functor that returns a std::unique_ptr<PropertyImpl>
// @tparam UpdateFunc functor that takes a PropertyImpl&.
template <typename NewFunc, typename UpdateFunc>
int CreateOrUpdateProperty(const wpi::Twine& name, NewFunc newFunc,
int CreateOrUpdateProperty(std::string_view name, NewFunc newFunc,
UpdateFunc updateFunc) {
wpi::SmallVector<char, 64> nameBuf;
int& ndx = m_properties[name.toStringRef(nameBuf)];
int& ndx = m_properties[name];
if (ndx == 0) {
// create a new index
ndx = m_propertyData.size() + 1;
@@ -91,7 +91,7 @@ class PropertyContainer {
return ndx;
}
template <typename NewFunc>
int CreateProperty(const wpi::Twine& name, NewFunc newFunc) {
int CreateProperty(std::string_view name, NewFunc newFunc) {
return CreateOrUpdateProperty(name, newFunc, [](PropertyImpl&) {});
}
@@ -100,7 +100,7 @@ class PropertyContainer {
// Note: called with m_mutex held.
// The default implementation simply creates a PropertyImpl object.
virtual std::unique_ptr<PropertyImpl> CreateEmptyProperty(
const wpi::Twine& name) const;
std::string_view name) const;
// Cache properties. Implementations must return false and set status to
// CS_SOURCE_IS_DISCONNECTED if not possible to cache.
@@ -111,7 +111,7 @@ class PropertyContainer {
// Update property value; must be called with m_mutex held.
virtual void UpdatePropertyValue(int property, bool setString, int value,
const wpi::Twine& valueStr) = 0;
std::string_view valueStr) = 0;
// Whether CacheProperties() has been successful at least once (and thus
// should not be called again)

View File

@@ -6,18 +6,18 @@
using namespace cs;
PropertyImpl::PropertyImpl(const wpi::Twine& name_) : name{name_.str()} {}
PropertyImpl::PropertyImpl(const wpi::Twine& name_, CS_PropertyKind kind_,
PropertyImpl::PropertyImpl(std::string_view name_) : name{name_} {}
PropertyImpl::PropertyImpl(std::string_view name_, CS_PropertyKind kind_,
int step_, int defaultValue_, int value_)
: name{name_.str()},
: name{name_},
propKind{kind_},
step{step_},
defaultValue{defaultValue_},
value{value_} {}
PropertyImpl::PropertyImpl(const wpi::Twine& name_, CS_PropertyKind kind_,
PropertyImpl::PropertyImpl(std::string_view name_, CS_PropertyKind kind_,
int minimum_, int maximum_, int step_,
int defaultValue_, int value_)
: name{name_.str()},
: name{name_},
propKind{kind_},
hasMinimum{true},
hasMaximum{true},
@@ -43,11 +43,10 @@ void PropertyImpl::SetValue(int v) {
}
}
void PropertyImpl::SetValue(const wpi::Twine& v) {
void PropertyImpl::SetValue(std::string_view v) {
bool valueChanged = false;
std::string vStr = v.str();
if (valueStr != vStr) {
valueStr = vStr;
if (valueStr != v) {
valueStr = v;
valueChanged = true;
}
bool wasValueSet = valueSet;

View File

@@ -6,11 +6,10 @@
#define CSCORE_PROPERTYIMPL_H_
#include <string>
#include <string_view>
#include <vector>
#include <wpi/Signal.h>
#include <wpi/StringRef.h>
#include <wpi/Twine.h>
#include "cscore_c.h"
@@ -20,17 +19,17 @@ namespace cs {
class PropertyImpl {
public:
PropertyImpl() = default;
explicit PropertyImpl(const wpi::Twine& name_);
PropertyImpl(const wpi::Twine& name_, CS_PropertyKind kind_, int step_,
explicit PropertyImpl(std::string_view name_);
PropertyImpl(std::string_view name_, CS_PropertyKind kind_, int step_,
int defaultValue_, int value_);
PropertyImpl(const wpi::Twine& name_, CS_PropertyKind kind_, int minimum_,
PropertyImpl(std::string_view name_, CS_PropertyKind kind_, int minimum_,
int maximum_, int step_, int defaultValue_, int value_);
virtual ~PropertyImpl() = default;
PropertyImpl(const PropertyImpl& oth) = delete;
PropertyImpl& operator=(const PropertyImpl& oth) = delete;
void SetValue(int v);
void SetValue(const wpi::Twine& v);
void SetValue(std::string_view v);
void SetDefaultValue(int v);
std::string name;

View File

@@ -10,14 +10,14 @@
using namespace cs;
RawSinkImpl::RawSinkImpl(const wpi::Twine& name, wpi::Logger& logger,
RawSinkImpl::RawSinkImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry)
: SinkImpl{name, logger, notifier, telemetry} {
m_active = true;
// m_thread = std::thread(&RawSinkImpl::ThreadMain, this);
}
RawSinkImpl::RawSinkImpl(const wpi::Twine& name, wpi::Logger& logger,
RawSinkImpl::RawSinkImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry,
std::function<void(uint64_t time)> processFrame)
: SinkImpl{name, logger, notifier, telemetry} {}
@@ -127,7 +127,7 @@ void RawSinkImpl::ThreadMain() {
std::this_thread::sleep_for(std::chrono::seconds(1));
continue;
}
SDEBUG4("waiting for frame");
SDEBUG4("{}", "waiting for frame");
Frame frame = source->GetNextFrame(); // blocks
if (!m_active) {
break;
@@ -143,14 +143,14 @@ void RawSinkImpl::ThreadMain() {
}
namespace cs {
CS_Sink CreateRawSink(const wpi::Twine& name, CS_Status* status) {
CS_Sink CreateRawSink(std::string_view name, CS_Status* status) {
auto& inst = Instance::GetInstance();
return inst.CreateSink(CS_SINK_RAW,
std::make_shared<RawSinkImpl>(
name, inst.logger, inst.notifier, inst.telemetry));
}
CS_Sink CreateRawSinkCallback(const wpi::Twine& name,
CS_Sink CreateRawSinkCallback(std::string_view name,
std::function<void(uint64_t time)> processFrame,
CS_Status* status) {
auto& inst = Instance::GetInstance();

View File

@@ -9,9 +9,9 @@
#include <atomic>
#include <functional>
#include <string_view>
#include <thread>
#include <wpi/Twine.h>
#include <wpi/condition_variable.h>
#include "Frame.h"
@@ -23,9 +23,9 @@ class SourceImpl;
class RawSinkImpl : public SinkImpl {
public:
RawSinkImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
RawSinkImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry);
RawSinkImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
RawSinkImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry,
std::function<void(uint64_t time)> processFrame);
~RawSinkImpl() override;

View File

@@ -14,7 +14,7 @@
using namespace cs;
RawSourceImpl::RawSourceImpl(const wpi::Twine& name, wpi::Logger& logger,
RawSourceImpl::RawSourceImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry,
const VideoMode& mode)
: ConfigurableSourceImpl{name, logger, notifier, telemetry, mode} {}
@@ -47,7 +47,7 @@ void RawSourceImpl::PutFrame(const CS_RawFrame& image) {
}
namespace cs {
CS_Source CreateRawSource(const wpi::Twine& name, const VideoMode& mode,
CS_Source CreateRawSource(std::string_view name, const VideoMode& mode,
CS_Status* status) {
auto& inst = Instance::GetInstance();
return inst.CreateSource(CS_SOURCE_RAW, std::make_shared<RawSourceImpl>(

View File

@@ -9,11 +9,9 @@
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include <wpi/ArrayRef.h>
#include <wpi/Twine.h>
#include "ConfigurableSourceImpl.h"
#include "SourceImpl.h"
#include "cscore_raw.h"
@@ -22,7 +20,7 @@ namespace cs {
class RawSourceImpl : public ConfigurableSourceImpl {
public:
RawSourceImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
RawSourceImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry, const VideoMode& mode);
~RawSourceImpl() override;

View File

@@ -12,12 +12,12 @@
using namespace cs;
SinkImpl::SinkImpl(const wpi::Twine& name, wpi::Logger& logger,
SinkImpl::SinkImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry)
: m_logger(logger),
m_notifier(notifier),
m_telemetry(telemetry),
m_name{name.str()} {}
m_name{name} {}
SinkImpl::~SinkImpl() {
if (m_source) {
@@ -28,15 +28,16 @@ SinkImpl::~SinkImpl() {
}
}
void SinkImpl::SetDescription(const wpi::Twine& description) {
void SinkImpl::SetDescription(std::string_view description) {
std::scoped_lock lock(m_mutex);
m_description = description.str();
m_description = description;
}
wpi::StringRef SinkImpl::GetDescription(wpi::SmallVectorImpl<char>& buf) const {
std::string_view SinkImpl::GetDescription(
wpi::SmallVectorImpl<char>& buf) const {
std::scoped_lock lock(m_mutex);
buf.append(m_description.begin(), m_description.end());
return wpi::StringRef{buf.data(), buf.size()};
return {buf.data(), buf.size()};
}
void SinkImpl::Enable() {
@@ -106,28 +107,27 @@ std::string SinkImpl::GetError() const {
if (!m_source) {
return "no source connected";
}
return m_source->GetCurFrame().GetError();
return std::string{m_source->GetCurFrame().GetError()};
}
wpi::StringRef SinkImpl::GetError(wpi::SmallVectorImpl<char>& buf) const {
std::string_view SinkImpl::GetError(wpi::SmallVectorImpl<char>& buf) const {
std::scoped_lock lock(m_mutex);
if (!m_source) {
return "no source connected";
}
// Make a copy as it's shared data
wpi::StringRef error = m_source->GetCurFrame().GetError();
std::string_view error = m_source->GetCurFrame().GetError();
buf.clear();
buf.append(error.data(), error.data() + error.size());
return wpi::StringRef{buf.data(), buf.size()};
return {buf.data(), buf.size()};
}
bool SinkImpl::SetConfigJson(wpi::StringRef config, CS_Status* status) {
bool SinkImpl::SetConfigJson(std::string_view config, CS_Status* status) {
wpi::json j;
try {
j = wpi::json::parse(config);
} catch (const wpi::json::parse_error& e) {
SWARNING("SetConfigJson: parse error at byte " << e.byte << ": "
<< e.what());
SWARNING("SetConfigJson: parse error at byte {}: {}", e.byte, e.what());
*status = CS_PROPERTY_WRITE_FAILED;
return false;
}
@@ -169,12 +169,12 @@ void SinkImpl::NotifyPropertyCreated(int propIndex, PropertyImpl& prop) {
if (prop.propKind == CS_PROP_ENUM) {
m_notifier.NotifySinkProperty(*this, CS_SINK_PROPERTY_CHOICES_UPDATED,
prop.name, propIndex, prop.propKind,
prop.value, wpi::Twine{});
prop.value, {});
}
}
void SinkImpl::UpdatePropertyValue(int property, bool setString, int value,
const wpi::Twine& valueStr) {
std::string_view valueStr) {
auto prop = GetProperty(property);
if (!prop) {
return;

View File

@@ -7,10 +7,9 @@
#include <memory>
#include <string>
#include <string_view>
#include <wpi/Logger.h>
#include <wpi/StringRef.h>
#include <wpi/Twine.h>
#include <wpi/mutex.h>
#include "SourceImpl.h"
@@ -27,16 +26,16 @@ class Telemetry;
class SinkImpl : public PropertyContainer {
public:
explicit SinkImpl(const wpi::Twine& name, wpi::Logger& logger,
explicit SinkImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry);
~SinkImpl() override;
SinkImpl(const SinkImpl& queue) = delete;
SinkImpl& operator=(const SinkImpl& queue) = delete;
wpi::StringRef GetName() const { return m_name; }
std::string_view GetName() const { return m_name; }
void SetDescription(const wpi::Twine& description);
wpi::StringRef GetDescription(wpi::SmallVectorImpl<char>& buf) const;
void SetDescription(std::string_view description);
std::string_view GetDescription(wpi::SmallVectorImpl<char>& buf) const;
void Enable();
void Disable();
@@ -50,9 +49,9 @@ class SinkImpl : public PropertyContainer {
}
std::string GetError() const;
wpi::StringRef GetError(wpi::SmallVectorImpl<char>& buf) const;
std::string_view GetError(wpi::SmallVectorImpl<char>& buf) const;
bool SetConfigJson(wpi::StringRef config, CS_Status* status);
bool SetConfigJson(std::string_view config, CS_Status* status);
virtual bool SetConfigJson(const wpi::json& config, CS_Status* status);
std::string GetConfigJson(CS_Status* status);
virtual wpi::json GetConfigJsonObject(CS_Status* status);
@@ -61,7 +60,7 @@ class SinkImpl : public PropertyContainer {
// PropertyContainer implementation
void NotifyPropertyCreated(int propIndex, PropertyImpl& prop) override;
void UpdatePropertyValue(int property, bool setString, int value,
const wpi::Twine& valueStr) override;
std::string_view valueStr) override;
virtual void SetSourceImpl(std::shared_ptr<SourceImpl> source);

View File

@@ -8,6 +8,7 @@
#include <cstring>
#include <memory>
#include <wpi/StringExtras.h>
#include <wpi/json.h>
#include <wpi/timestamp.h>
@@ -19,13 +20,13 @@ using namespace cs;
static constexpr size_t kMaxImagesAvail = 32;
SourceImpl::SourceImpl(const wpi::Twine& name, wpi::Logger& logger,
SourceImpl::SourceImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry)
: m_logger(logger),
m_notifier(notifier),
m_telemetry(telemetry),
m_name{name.str()} {
m_frame = Frame{*this, wpi::StringRef{}, 0};
m_name{name} {
m_frame = Frame{*this, std::string_view{}, 0};
}
SourceImpl::~SourceImpl() {
@@ -41,16 +42,16 @@ SourceImpl::~SourceImpl() {
// Everything else can clean up itself.
}
void SourceImpl::SetDescription(const wpi::Twine& description) {
void SourceImpl::SetDescription(std::string_view description) {
std::scoped_lock lock(m_mutex);
m_description = description.str();
m_description = description;
}
wpi::StringRef SourceImpl::GetDescription(
std::string_view SourceImpl::GetDescription(
wpi::SmallVectorImpl<char>& buf) const {
std::scoped_lock lock(m_mutex);
buf.append(m_description.begin(), m_description.end());
return wpi::StringRef{buf.data(), buf.size()};
return {buf.data(), buf.size()};
}
void SourceImpl::SetConnected(bool connected) {
@@ -93,7 +94,7 @@ Frame SourceImpl::GetNextFrame(double timeout) {
void SourceImpl::Wakeup() {
{
std::scoped_lock lock{m_frameMutex};
m_frame = Frame{*this, wpi::StringRef{}, 0};
m_frame = Frame{*this, std::string_view{}, 0};
}
m_frameCv.notify_all();
}
@@ -168,13 +169,12 @@ bool SourceImpl::SetFPS(int fps, CS_Status* status) {
return SetVideoMode(mode, status);
}
bool SourceImpl::SetConfigJson(wpi::StringRef config, CS_Status* status) {
bool SourceImpl::SetConfigJson(std::string_view config, CS_Status* status) {
wpi::json j;
try {
j = wpi::json::parse(config);
} catch (const wpi::json::parse_error& e) {
SWARNING("SetConfigJson: parse error at byte " << e.byte << ": "
<< e.what());
SWARNING("SetConfigJson: parse error at byte {}: {}", e.byte, e.what());
*status = CS_PROPERTY_WRITE_FAILED;
return false;
}
@@ -188,23 +188,22 @@ bool SourceImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
if (config.count("pixel format") != 0) {
try {
auto str = config.at("pixel format").get<std::string>();
wpi::StringRef s(str);
if (s.equals_lower("mjpeg")) {
if (wpi::equals_lower(str, "mjpeg")) {
mode.pixelFormat = cs::VideoMode::kMJPEG;
} else if (s.equals_lower("yuyv")) {
} else if (wpi::equals_lower(str, "yuyv")) {
mode.pixelFormat = cs::VideoMode::kYUYV;
} else if (s.equals_lower("rgb565")) {
} else if (wpi::equals_lower(str, "rgb565")) {
mode.pixelFormat = cs::VideoMode::kRGB565;
} else if (s.equals_lower("bgr")) {
} else if (wpi::equals_lower(str, "bgr")) {
mode.pixelFormat = cs::VideoMode::kBGR;
} else if (s.equals_lower("gray")) {
} else if (wpi::equals_lower(str, "gray")) {
mode.pixelFormat = cs::VideoMode::kGray;
} else {
SWARNING("SetConfigJson: could not understand pixel format value '"
<< str << '\'');
SWARNING("SetConfigJson: could not understand pixel format value '{}'",
str);
}
} catch (const wpi::json::exception& e) {
SWARNING("SetConfigJson: could not read pixel format: " << e.what());
SWARNING("SetConfigJson: could not read pixel format: {}", e.what());
}
}
@@ -213,7 +212,7 @@ bool SourceImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
try {
mode.width = config.at("width").get<unsigned int>();
} catch (const wpi::json::exception& e) {
SWARNING("SetConfigJson: could not read width: " << e.what());
SWARNING("SetConfigJson: could not read width: {}", e.what());
}
}
@@ -222,7 +221,7 @@ bool SourceImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
try {
mode.height = config.at("height").get<unsigned int>();
} catch (const wpi::json::exception& e) {
SWARNING("SetConfigJson: could not read height: " << e.what());
SWARNING("SetConfigJson: could not read height: {}", e.what());
}
}
@@ -231,30 +230,31 @@ bool SourceImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
try {
mode.fps = config.at("fps").get<unsigned int>();
} catch (const wpi::json::exception& e) {
SWARNING("SetConfigJson: could not read fps: " << e.what());
SWARNING("SetConfigJson: could not read fps: {}", e.what());
}
}
// if all of video mode is set, use SetVideoMode, otherwise piecemeal it
if (mode.pixelFormat != VideoMode::kUnknown && mode.width != 0 &&
mode.height != 0 && mode.fps != 0) {
SINFO("SetConfigJson: setting video mode to pixelFormat "
<< mode.pixelFormat << ", width " << mode.width << ", height "
<< mode.height << ", fps " << mode.fps);
SINFO(
"SetConfigJson: setting video mode to pixelFormat {}, width {}, height "
"{}, fps {}",
mode.pixelFormat, mode.width, mode.height, mode.fps);
SetVideoMode(mode, status);
} else {
if (mode.pixelFormat != cs::VideoMode::kUnknown) {
SINFO("SetConfigJson: setting pixelFormat " << mode.pixelFormat);
SINFO("SetConfigJson: setting pixelFormat {}", mode.pixelFormat);
SetPixelFormat(static_cast<cs::VideoMode::PixelFormat>(mode.pixelFormat),
status);
}
if (mode.width != 0 && mode.height != 0) {
SINFO("SetConfigJson: setting width " << mode.width << ", height "
<< mode.height);
SINFO("SetConfigJson: setting width {}, height {}", mode.width,
mode.height);
SetResolution(mode.width, mode.height, status);
}
if (mode.fps != 0) {
SINFO("SetConfigJson: setting fps " << mode.fps);
SINFO("SetConfigJson: setting fps {}", mode.fps);
SetFPS(mode.fps, status);
}
}
@@ -263,10 +263,10 @@ bool SourceImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
if (config.count("brightness") != 0) {
try {
int val = config.at("brightness").get<int>();
SINFO("SetConfigJson: setting brightness to " << val);
SINFO("SetConfigJson: setting brightness to {}", val);
SetBrightness(val, status);
} catch (const wpi::json::exception& e) {
SWARNING("SetConfigJson: could not read brightness: " << e.what());
SWARNING("SetConfigJson: could not read brightness: {}", e.what());
}
}
@@ -276,24 +276,24 @@ bool SourceImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
auto& setting = config.at("white balance");
if (setting.is_string()) {
auto str = setting.get<std::string>();
wpi::StringRef s(str);
if (s.equals_lower("auto")) {
SINFO("SetConfigJson: setting white balance to auto");
if (wpi::equals_lower(str, "auto")) {
SINFO("SetConfigJson: setting white balance to {}", "auto");
SetWhiteBalanceAuto(status);
} else if (s.equals_lower("hold")) {
SINFO("SetConfigJson: setting white balance to hold current");
} else if (wpi::equals_lower(str, "hold")) {
SINFO("SetConfigJson: setting white balance to {}", "hold current");
SetWhiteBalanceHoldCurrent(status);
} else {
SWARNING("SetConfigJson: could not understand white balance value '"
<< str << '\'');
SWARNING(
"SetConfigJson: could not understand white balance value '{}'",
str);
}
} else {
int val = setting.get<int>();
SINFO("SetConfigJson: setting white balance to " << val);
SINFO("SetConfigJson: setting white balance to {}", val);
SetWhiteBalanceManual(val, status);
}
} catch (const wpi::json::exception& e) {
SWARNING("SetConfigJson: could not read white balance: " << e.what());
SWARNING("SetConfigJson: could not read white balance: {}", e.what());
}
}
@@ -303,24 +303,23 @@ bool SourceImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
auto& setting = config.at("exposure");
if (setting.is_string()) {
auto str = setting.get<std::string>();
wpi::StringRef s(str);
if (s.equals_lower("auto")) {
SINFO("SetConfigJson: setting exposure to auto");
if (wpi::equals_lower(str, "auto")) {
SINFO("SetConfigJson: setting exposure to {}", "auto");
SetExposureAuto(status);
} else if (s.equals_lower("hold")) {
SINFO("SetConfigJson: setting exposure to hold current");
} else if (wpi::equals_lower(str, "hold")) {
SINFO("SetConfigJson: setting exposure to {}", "hold current");
SetExposureHoldCurrent(status);
} else {
SWARNING("SetConfigJson: could not understand exposure value '"
<< str << '\'');
SWARNING("SetConfigJson: could not understand exposure value '{}'",
str);
}
} else {
int val = setting.get<int>();
SINFO("SetConfigJson: setting exposure to " << val);
SINFO("SetConfigJson: setting exposure to {}", val);
SetExposureManual(val, status);
}
} catch (const wpi::json::exception& e) {
SWARNING("SetConfigJson: could not read exposure: " << e.what());
SWARNING("SetConfigJson: could not read exposure: {}", e.what());
}
}
@@ -344,7 +343,7 @@ wpi::json SourceImpl::GetConfigJsonObject(CS_Status* status) {
wpi::json j;
// pixel format
wpi::StringRef pixelFormat;
std::string_view pixelFormat;
switch (m_mode.pixelFormat) {
case VideoMode::kMJPEG:
pixelFormat = "mjpeg";
@@ -440,14 +439,12 @@ std::unique_ptr<Image> SourceImpl::AllocImage(
}
void SourceImpl::PutFrame(VideoMode::PixelFormat pixelFormat, int width,
int height, wpi::StringRef data, Frame::Time time) {
int height, std::string_view data, Frame::Time time) {
auto image = AllocImage(pixelFormat, width, height, data.size());
// Copy in image data
SDEBUG4("Copying data to "
<< reinterpret_cast<const void*>(image->data()) << " from "
<< reinterpret_cast<const void*>(data.data()) << " (" << data.size()
<< " bytes)");
SDEBUG4("Copying data to {} from {} ({} bytes)", fmt::ptr(image->data()),
fmt::ptr(data.data()), data.size());
std::memcpy(image->data(), data.data(), data.size());
PutFrame(std::move(image), time);
@@ -468,7 +465,7 @@ void SourceImpl::PutFrame(std::unique_ptr<Image> image, Frame::Time time) {
m_frameCv.notify_all();
}
void SourceImpl::PutError(const wpi::Twine& msg, Frame::Time time) {
void SourceImpl::PutError(std::string_view msg, Frame::Time time) {
// Update frame
{
std::scoped_lock lock{m_frameMutex};
@@ -487,12 +484,12 @@ void SourceImpl::NotifyPropertyCreated(int propIndex, PropertyImpl& prop) {
if (prop.propKind == CS_PROP_ENUM) {
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CHOICES_UPDATED,
prop.name, propIndex, prop.propKind,
prop.value, wpi::Twine{});
prop.value, {});
}
}
void SourceImpl::UpdatePropertyValue(int property, bool setString, int value,
const wpi::Twine& valueStr) {
std::string_view valueStr) {
auto prop = GetProperty(property);
if (!prop) {
return;

View File

@@ -9,12 +9,10 @@
#include <cstddef>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include <wpi/ArrayRef.h>
#include <wpi/Logger.h>
#include <wpi/StringRef.h>
#include <wpi/Twine.h>
#include <wpi/condition_variable.h>
#include <wpi/mutex.h>
@@ -37,7 +35,7 @@ class SourceImpl : public PropertyContainer {
friend class Frame;
public:
SourceImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
SourceImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry);
~SourceImpl() override;
SourceImpl(const SourceImpl& oth) = delete;
@@ -45,10 +43,10 @@ class SourceImpl : public PropertyContainer {
virtual void Start() = 0;
wpi::StringRef GetName() const { return m_name; }
std::string_view GetName() const { return m_name; }
void SetDescription(const wpi::Twine& description);
wpi::StringRef GetDescription(wpi::SmallVectorImpl<char>& buf) const;
void SetDescription(std::string_view description);
std::string_view GetDescription(wpi::SmallVectorImpl<char>& buf) const;
void SetConnectionStrategy(CS_ConnectionStrategy strategy) {
m_strategy = static_cast<int>(strategy);
@@ -128,7 +126,7 @@ class SourceImpl : public PropertyContainer {
virtual bool SetResolution(int width, int height, CS_Status* status);
virtual bool SetFPS(int fps, CS_Status* status);
bool SetConfigJson(wpi::StringRef config, CS_Status* status);
bool SetConfigJson(std::string_view config, CS_Status* status);
virtual bool SetConfigJson(const wpi::json& config, CS_Status* status);
std::string GetConfigJson(CS_Status* status);
virtual wpi::json GetConfigJsonObject(CS_Status* status);
@@ -141,12 +139,12 @@ class SourceImpl : public PropertyContainer {
protected:
void NotifyPropertyCreated(int propIndex, PropertyImpl& prop) override;
void UpdatePropertyValue(int property, bool setString, int value,
const wpi::Twine& valueStr) override;
std::string_view valueStr) override;
void PutFrame(VideoMode::PixelFormat pixelFormat, int width, int height,
wpi::StringRef data, Frame::Time time);
std::string_view data, Frame::Time time);
void PutFrame(std::unique_ptr<Image> image, Frame::Time time);
void PutError(const wpi::Twine& msg, Frame::Time time);
void PutError(std::string_view msg, Frame::Time time);
// Notification functions for corresponding atomics
virtual void NumSinksChanged() = 0;

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