mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-26 01:51:41 +00:00
Compare commits
152 Commits
v2021.3.1
...
v2022.0.0-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53b4891a5e | ||
|
|
646ded9123 | ||
|
|
ea0b8f48e6 | ||
|
|
2067d7e300 | ||
|
|
866571ab41 | ||
|
|
4e1fa03087 | ||
|
|
b45572167d | ||
|
|
57a160f1b3 | ||
|
|
29ae8640d9 | ||
|
|
ee6377e54b | ||
|
|
b0f1ae7ea3 | ||
|
|
7aae2b72dc | ||
|
|
73fcbbd748 | ||
|
|
e7bedde835 | ||
|
|
7253edb1e1 | ||
|
|
efa28125c6 | ||
|
|
9832fcfe14 | ||
|
|
49c71f9f2d | ||
|
|
791770cf6e | ||
|
|
9ce9188ff6 | ||
|
|
362066a9b7 | ||
|
|
26ff9371d9 | ||
|
|
4a36f86c81 | ||
|
|
85144e47ff | ||
|
|
b417d961ec | ||
|
|
ef4ea84cb5 | ||
|
|
b422665a3c | ||
|
|
186dadf14d | ||
|
|
04e64db945 | ||
|
|
f60994ad24 | ||
|
|
cfa1ca96f2 | ||
|
|
4d9ff76433 | ||
|
|
9e1b7e0464 | ||
|
|
a77c6ff3a2 | ||
|
|
099fde97d5 | ||
|
|
f8fc2463ee | ||
|
|
e246b78846 | ||
|
|
c1e128bd5a | ||
|
|
8284075ee4 | ||
|
|
f7db09a128 | ||
|
|
f9c3d54bd1 | ||
|
|
0773f4033e | ||
|
|
d068fb321f | ||
|
|
8d054c940c | ||
|
|
80f1d79218 | ||
|
|
64f5413253 | ||
|
|
2abbbd9e70 | ||
|
|
a5c471af7e | ||
|
|
edd2f0232c | ||
|
|
b2c3b2dd8e | ||
|
|
4f1cecb8e7 | ||
|
|
b336eac343 | ||
|
|
2a09f6fa45 | ||
|
|
0e702eb799 | ||
|
|
dea841103d | ||
|
|
82856cf816 | ||
|
|
8aecda03ed | ||
|
|
5c817082a0 | ||
|
|
15c521a7fe | ||
|
|
989de4a1bf | ||
|
|
d9eeb45b03 | ||
|
|
fe570e000c | ||
|
|
01dc0249de | ||
|
|
93523d572e | ||
|
|
4f7a4464df | ||
|
|
e09293a15e | ||
|
|
827b17a52b | ||
|
|
a610379965 | ||
|
|
4e2c3051be | ||
|
|
50915cb7ed | ||
|
|
f4e2d26d58 | ||
|
|
cb0051ae60 | ||
|
|
a238cec12b | ||
|
|
393bf23c0c | ||
|
|
e7d9ba135c | ||
|
|
0a0003c110 | ||
|
|
7e1b27554c | ||
|
|
fb2a56e2d6 | ||
|
|
84218bfb45 | ||
|
|
dd78243406 | ||
|
|
484cf9c0e8 | ||
|
|
a04d1b4f97 | ||
|
|
831c10bdfc | ||
|
|
87603e400d | ||
|
|
4426216725 | ||
|
|
bc15b953b4 | ||
|
|
6d20b12043 | ||
|
|
2385c2a430 | ||
|
|
87384ea684 | ||
|
|
04dae799a2 | ||
|
|
0768c39036 | ||
|
|
8dd8d4d2d4 | ||
|
|
49b06beedf | ||
|
|
4c562a4457 | ||
|
|
fdbbf11887 | ||
|
|
f1e64b349a | ||
|
|
224f3a05cf | ||
|
|
ff56d6861d | ||
|
|
1873fbefba | ||
|
|
80b479e502 | ||
|
|
1f7c9adeeb | ||
|
|
9ebc3b058d | ||
|
|
e21b443a45 | ||
|
|
da590120c4 | ||
|
|
561d53885e | ||
|
|
44ad67ca8c | ||
|
|
3fe8fc75aa | ||
|
|
3cc2da3328 | ||
|
|
a3cd90dd71 | ||
|
|
d6cfdd3bae | ||
|
|
ba08baabb9 | ||
|
|
497b712f67 | ||
|
|
f00dfed7ac | ||
|
|
3c08461685 | ||
|
|
5ef2b4fdc0 | ||
|
|
23d2326d1d | ||
|
|
e338f9f190 | ||
|
|
c8ff626fe2 | ||
|
|
4e424d51f4 | ||
|
|
6b50323b07 | ||
|
|
65c148536d | ||
|
|
f99f62bee4 | ||
|
|
365f5449ca | ||
|
|
ff52f207cc | ||
|
|
ee0eed143a | ||
|
|
5127380727 | ||
|
|
ced654880c | ||
|
|
05ebe93180 | ||
|
|
8d961dfd25 | ||
|
|
0abf6c9045 | ||
|
|
b7b178f49c | ||
|
|
687066af3d | ||
|
|
6b168ab0c8 | ||
|
|
3abe0b9d49 | ||
|
|
d7fabe81fe | ||
|
|
1dc81669c2 | ||
|
|
397e569aaf | ||
|
|
79267f9e60 | ||
|
|
48ebe5736a | ||
|
|
c2064c78b2 | ||
|
|
36608a283b | ||
|
|
6137f98eb5 | ||
|
|
a6f6539691 | ||
|
|
a79faace1b | ||
|
|
9550777b9d | ||
|
|
160fb740f4 | ||
|
|
48e9f39513 | ||
|
|
8afa596fdf | ||
|
|
d3e45c297c | ||
|
|
da96707dca | ||
|
|
21624ef273 | ||
|
|
6e23e1840a |
@@ -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,
|
||||
|
||||
2
.github/workflows/cmake.yml
vendored
2
.github/workflows/cmake.yml
vendored
@@ -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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: "Validate Gradle Wrapper"
|
||||
on: [push, pull_request]
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
validation:
|
||||
|
||||
29
.github/workflows/lint-format.yml
vendored
29
.github/workflows/lint-format.yml
vendored
@@ -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
|
||||
|
||||
@@ -23,6 +23,7 @@ includeOtherLibs {
|
||||
^cameraserver/
|
||||
^cscore
|
||||
^drake/
|
||||
^fmt/
|
||||
^hal/
|
||||
^imgui
|
||||
^implot
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -5,5 +5,5 @@ repositories {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation "edu.wpi.first:native-utils:2021.1.1"
|
||||
implementation "edu.wpi.first:native-utils:2022.0.2"
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ repoRootNameOverride {
|
||||
}
|
||||
|
||||
includeOtherLibs {
|
||||
^fmt/
|
||||
^hal/
|
||||
^networktables/
|
||||
^opencv2/
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* implements VisionRunner.Listener<MyFindTotePipeline> {
|
||||
*
|
||||
* // 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);
|
||||
* }
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
|
||||
@@ -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()
|
||||
|
||||
118
crossConnIntegrationTests/build.gradle
Normal file
118
crossConnIntegrationTests/build.gradle
Normal 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) {
|
||||
}
|
||||
}
|
||||
112
crossConnIntegrationTests/src/main/native/cpp/AnalogTests.cpp
Normal file
112
crossConnIntegrationTests/src/main/native/cpp/AnalogTests.cpp
Normal 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));
|
||||
101
crossConnIntegrationTests/src/main/native/cpp/DIOTests.cpp
Normal file
101
crossConnIntegrationTests/src/main/native/cpp/DIOTests.cpp
Normal 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));
|
||||
@@ -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));
|
||||
@@ -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();
|
||||
}
|
||||
357
crossConnIntegrationTests/src/main/native/cpp/PWMTests.cpp
Normal file
357
crossConnIntegrationTests/src/main/native/cpp/PWMTests.cpp
Normal 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));
|
||||
@@ -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));
|
||||
@@ -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));
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -31,6 +31,7 @@ repoRootNameOverride {
|
||||
}
|
||||
|
||||
includeOtherLibs {
|
||||
^fmt/
|
||||
^opencv2/
|
||||
^support/
|
||||
^tcpsockets/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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. */
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
@@ -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];
|
||||
@@ -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];
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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.
|
||||
@@ -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, {});
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
15
cscore/src/main/native/cpp/Log.cpp
Normal file
15
cscore/src/main/native/cpp/Log.cpp
Normal 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());
|
||||
}
|
||||
@@ -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_
|
||||
|
||||
@@ -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¶m2=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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user