mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-04 03:11:43 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60c2f59051 | ||
|
|
d55ca191b8 | ||
|
|
e8b24717c7 | ||
|
|
182758c05b | ||
|
|
74f7ba04b0 | ||
|
|
997d4fdf47 | ||
|
|
76d9e26633 | ||
|
|
a230c814cb | ||
|
|
12cb77cd7c | ||
|
|
8a9822a96b | ||
|
|
a9371a7586 | ||
|
|
6992f5421f | ||
|
|
43696956d2 | ||
|
|
ae3fd5adac | ||
|
|
404666b298 | ||
|
|
1eb4c99d15 | ||
|
|
910b9f3af7 | ||
|
|
09d90b02fb | ||
|
|
0e1f9c2ed2 | ||
|
|
f156a00117 | ||
|
|
4a6087ed56 | ||
|
|
88a09dd13a | ||
|
|
7d19596367 | ||
|
|
bd05dfa1c7 | ||
|
|
05d6660a6b | ||
|
|
1349dd4bd8 | ||
|
|
fdf298b172 | ||
|
|
453a9047e4 |
6
.wpilib/wpilib_preferences.json
Normal file
6
.wpilib/wpilib_preferences.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"enableCppIntellisense": true,
|
||||||
|
"currentLanguage": "cpp",
|
||||||
|
"projectYear": "2019",
|
||||||
|
"teamNumber": 0
|
||||||
|
}
|
||||||
19
README.md
19
README.md
@@ -25,10 +25,10 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
|
|||||||
|
|
||||||
- A C++ compiler
|
- A C++ compiler
|
||||||
- On Linux, GCC works fine
|
- On Linux, GCC works fine
|
||||||
- On Windows, you need Visual Studio 2015 (the free community edition works fine).
|
- On Windows, you need Visual Studio 2017 (the free community edition works fine).
|
||||||
Make sure to select the C++ Programming Language for installation
|
Make sure to select the C++ Programming Language for installation
|
||||||
- [ARM Compiler Toolchain](http://first.wpi.edu/FRC/roborio/toolchains/)
|
- [ARM Compiler Toolchain](https://github.com/wpilibsuite/toolchain-builder/releases)
|
||||||
* Note that for 2017-2018 and beyond, you will need version 5 or greater of GCC
|
* Note that for 2019 and beyond, you should use version 6 or greater of GCC
|
||||||
- Doxygen (Only required if you want to build the C++ documentation)
|
- Doxygen (Only required if you want to build the C++ documentation)
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
@@ -79,23 +79,18 @@ There are a few tasks other than `build` available. To see them, run the meta-ta
|
|||||||
|
|
||||||
wpiformat can be executed anywhere in the repository via `py -3 -m wpiformat` on Windows or `python3 -m wpiformat` on other platforms.
|
wpiformat can be executed anywhere in the repository via `py -3 -m wpiformat` on Windows or `python3 -m wpiformat` on other platforms.
|
||||||
|
|
||||||
|
CMake is also supported for building. See [README-CMAKE.md](README-CMAKE.md).
|
||||||
|
|
||||||
## Publishing
|
## Publishing
|
||||||
|
|
||||||
If you are building to test with the Eclipse plugins or just want to export the build as a Maven-style dependency, simply run the `publish` task. This task will publish all available packages to ~/releases/maven/development. If you need to publish the project to a different repo, you can specify it with `-Prepo=repo_name`. Valid options are:
|
If you are building to test with other dependencies or just want to export the build as a Maven-style dependency, simply run the `publish` task. This task will publish all available packages to ~/releases/maven/development. If you need to publish the project to a different repo, you can specify it with `-Prepo=repo_name`. Valid options are:
|
||||||
|
|
||||||
- development - The default repo.
|
- development - The default repo.
|
||||||
- beta - Publishes to ~/releases/maven/beta.
|
- beta - Publishes to ~/releases/maven/beta.
|
||||||
- stable - Publishes to ~/releases/maven/stable.
|
- stable - Publishes to ~/releases/maven/stable.
|
||||||
- release - Publishes to ~/releases/maven/release.
|
- release - Publishes to ~/releases/maven/release.
|
||||||
|
|
||||||
The following maven targets a published by this task:
|
The maven artifacts are described in [MavenArtifacts.md](MavenArtifacts.md)
|
||||||
|
|
||||||
- edu.wpi.first.wpilib.cmake:cpp-root:1.0.0 - roboRIO C++
|
|
||||||
- edu.wpi.first.wpilibc.simulation:WPILibCSim:0.1.0 - Simulation C++
|
|
||||||
- edu.wpi.first.wpilibj:wpilibJavaFinal:0.1.0-SNAPSHOT - roboRIO Java
|
|
||||||
- edu.wpi.first.wpilibj:wpilibJavaSim:0.1.0-SNAPSHOT - Simulation Java
|
|
||||||
- edu.wpi.first.wpilibj.simulation:SimDS:0.1.0-SNAPSHOT - The driverstation for controlling simulation.
|
|
||||||
- org.gazebosim:JavaGazebo:0.1.0-SNAPSHOT - Gazebo protocol for Java.
|
|
||||||
|
|
||||||
## Structure and Organization
|
## Structure and Organization
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ public final class CameraServer {
|
|||||||
private final Map<String, VideoSource> m_sources;
|
private final Map<String, VideoSource> m_sources;
|
||||||
private final Map<String, VideoSink> m_sinks;
|
private final Map<String, VideoSink> m_sinks;
|
||||||
private final Map<Integer, NetworkTable> m_tables; // indexed by source handle
|
private final Map<Integer, NetworkTable> m_tables; // indexed by source handle
|
||||||
|
// source handle indexed by sink handle
|
||||||
|
private final Map<Integer, Integer> m_fixedSources;
|
||||||
private final NetworkTable m_publishTable;
|
private final NetworkTable m_publishTable;
|
||||||
private final VideoListener m_videoListener; //NOPMD
|
private final VideoListener m_videoListener; //NOPMD
|
||||||
private final int m_tableListener; //NOPMD
|
private final int m_tableListener; //NOPMD
|
||||||
@@ -157,14 +159,20 @@ public final class CameraServer {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP"})
|
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP", "PMD.CyclomaticComplexity"})
|
||||||
private synchronized void updateStreamValues() {
|
private synchronized void updateStreamValues() {
|
||||||
// Over all the sinks...
|
// Over all the sinks...
|
||||||
for (VideoSink i : m_sinks.values()) {
|
for (VideoSink i : m_sinks.values()) {
|
||||||
int sink = i.getHandle();
|
int sink = i.getHandle();
|
||||||
|
|
||||||
// Get the source's subtable (if none exists, we're done)
|
// Get the source's subtable (if none exists, we're done)
|
||||||
int source = CameraServerJNI.getSinkSource(sink);
|
int source;
|
||||||
|
Integer fixedSource = m_fixedSources.get(sink);
|
||||||
|
if (fixedSource != null) {
|
||||||
|
source = fixedSource;
|
||||||
|
} else {
|
||||||
|
source = CameraServerJNI.getSinkSource(sink);
|
||||||
|
}
|
||||||
if (source == 0) {
|
if (source == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -295,6 +303,7 @@ public final class CameraServer {
|
|||||||
m_defaultUsbDevice = new AtomicInteger();
|
m_defaultUsbDevice = new AtomicInteger();
|
||||||
m_sources = new Hashtable<>();
|
m_sources = new Hashtable<>();
|
||||||
m_sinks = new Hashtable<>();
|
m_sinks = new Hashtable<>();
|
||||||
|
m_fixedSources = new Hashtable<>();
|
||||||
m_tables = new Hashtable<>();
|
m_tables = new Hashtable<>();
|
||||||
m_publishTable = NetworkTableInstance.getDefault().getTable(kPublishName);
|
m_publishTable = NetworkTableInstance.getDefault().getTable(kPublishName);
|
||||||
m_nextPort = kBasePort;
|
m_nextPort = kBasePort;
|
||||||
@@ -537,10 +546,11 @@ public final class CameraServer {
|
|||||||
*
|
*
|
||||||
* @param camera Camera
|
* @param camera Camera
|
||||||
*/
|
*/
|
||||||
public void startAutomaticCapture(VideoSource camera) {
|
public MjpegServer startAutomaticCapture(VideoSource camera) {
|
||||||
addCamera(camera);
|
addCamera(camera);
|
||||||
VideoSink server = addServer("serve_" + camera.getName());
|
MjpegServer server = addServer("serve_" + camera.getName());
|
||||||
server.setSource(camera);
|
server.setSource(camera);
|
||||||
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -595,6 +605,21 @@ public final class CameraServer {
|
|||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a virtual camera for switching between two streams. Unlike the
|
||||||
|
* other addCamera methods, this returns a VideoSink rather than a
|
||||||
|
* VideoSource. Calling setSource() on the returned object can be used
|
||||||
|
* to switch the actual source of the stream.
|
||||||
|
*/
|
||||||
|
public MjpegServer addSwitchedCamera(String name) {
|
||||||
|
// create a dummy CvSource
|
||||||
|
CvSource source = new CvSource(name, VideoMode.PixelFormat.kMJPEG, 160, 120, 30);
|
||||||
|
MjpegServer server = startAutomaticCapture(source);
|
||||||
|
m_fixedSources.put(server.getHandle(), source.getHandle());
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get OpenCV access to the primary camera feed. This allows you to
|
* Get OpenCV access to the primary camera feed. This allows you to
|
||||||
* get images from the camera for image processing on the roboRIO.
|
* get images from the camera for image processing on the roboRIO.
|
||||||
|
|||||||
@@ -33,10 +33,11 @@ struct CameraServer::Impl {
|
|||||||
void UpdateStreamValues();
|
void UpdateStreamValues();
|
||||||
|
|
||||||
wpi::mutex m_mutex;
|
wpi::mutex m_mutex;
|
||||||
std::atomic<int> m_defaultUsbDevice;
|
std::atomic<int> m_defaultUsbDevice{0};
|
||||||
std::string m_primarySourceName;
|
std::string m_primarySourceName;
|
||||||
wpi::StringMap<cs::VideoSource> m_sources;
|
wpi::StringMap<cs::VideoSource> m_sources;
|
||||||
wpi::StringMap<cs::VideoSink> m_sinks;
|
wpi::StringMap<cs::VideoSink> m_sinks;
|
||||||
|
wpi::DenseMap<CS_Sink, CS_Source> m_fixedSources;
|
||||||
wpi::DenseMap<CS_Source, std::shared_ptr<nt::NetworkTable>> m_tables;
|
wpi::DenseMap<CS_Source, std::shared_ptr<nt::NetworkTable>> m_tables;
|
||||||
std::shared_ptr<nt::NetworkTable> m_publishTable;
|
std::shared_ptr<nt::NetworkTable> m_publishTable;
|
||||||
cs::VideoListener m_videoListener;
|
cs::VideoListener m_videoListener;
|
||||||
@@ -156,7 +157,8 @@ void CameraServer::Impl::UpdateStreamValues() {
|
|||||||
CS_Sink sink = i.second.GetHandle();
|
CS_Sink sink = i.second.GetHandle();
|
||||||
|
|
||||||
// Get the source's subtable (if none exists, we're done)
|
// Get the source's subtable (if none exists, we're done)
|
||||||
CS_Source source = cs::GetSinkSource(sink, &status);
|
CS_Source source = m_fixedSources.lookup(sink);
|
||||||
|
if (source == 0) source = cs::GetSinkSource(sink, &status);
|
||||||
if (source == 0) continue;
|
if (source == 0) continue;
|
||||||
auto table = m_tables.lookup(source);
|
auto table = m_tables.lookup(source);
|
||||||
if (table) {
|
if (table) {
|
||||||
@@ -538,10 +540,21 @@ cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
|||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraServer::StartAutomaticCapture(const cs::VideoSource& camera) {
|
cs::MjpegServer CameraServer::AddSwitchedCamera(const wpi::Twine& 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();
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
cs::MjpegServer CameraServer::StartAutomaticCapture(
|
||||||
|
const cs::VideoSource& camera) {
|
||||||
AddCamera(camera);
|
AddCamera(camera);
|
||||||
auto server = AddServer(wpi::Twine("serve_") + camera.GetName());
|
auto server = AddServer(wpi::Twine("serve_") + camera.GetName());
|
||||||
server.SetSource(camera);
|
server.SetSource(camera);
|
||||||
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::CvSink CameraServer::GetVideo() {
|
cs::CvSink CameraServer::GetVideo() {
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ class CameraServer {
|
|||||||
*
|
*
|
||||||
* @param camera Camera
|
* @param camera Camera
|
||||||
*/
|
*/
|
||||||
void StartAutomaticCapture(const cs::VideoSource& camera);
|
cs::MjpegServer StartAutomaticCapture(const cs::VideoSource& camera);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an Axis IP camera.
|
* Adds an Axis IP camera.
|
||||||
@@ -173,6 +173,14 @@ class CameraServer {
|
|||||||
cs::AxisCamera AddAxisCamera(const wpi::Twine& name,
|
cs::AxisCamera AddAxisCamera(const wpi::Twine& name,
|
||||||
std::initializer_list<T> hosts);
|
std::initializer_list<T> hosts);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
cs::MjpegServer AddSwitchedCamera(const wpi::Twine& name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get OpenCV access to the primary camera feed. This allows you to
|
* Get OpenCV access to the primary camera feed. This allows you to
|
||||||
* get images from the camera for image processing on the roboRIO.
|
* get images from the camera for image processing on the roboRIO.
|
||||||
|
|||||||
@@ -147,6 +147,8 @@ public class CameraServerJNI {
|
|||||||
public static native String getSinkDescription(int sink);
|
public static native String getSinkDescription(int sink);
|
||||||
public static native int getSinkProperty(int sink, String name);
|
public static native int getSinkProperty(int sink, String name);
|
||||||
public static native int[] enumerateSinkProperties(int sink);
|
public static native int[] enumerateSinkProperties(int sink);
|
||||||
|
public static native boolean setSinkConfigJson(int sink, String config);
|
||||||
|
public static native String getSinkConfigJson(int sink);
|
||||||
public static native void setSinkSource(int sink, int source);
|
public static native void setSinkSource(int sink, int source);
|
||||||
public static native int getSinkSourceProperty(int sink, String name);
|
public static native int getSinkSourceProperty(int sink, String name);
|
||||||
public static native int getSinkSource(int sink);
|
public static native int getSinkSource(int sink);
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class MjpegServer extends VideoSink {
|
|||||||
* @param width width, 0 for unspecified
|
* @param width width, 0 for unspecified
|
||||||
* @param height height, 0 for unspecified
|
* @param height height, 0 for unspecified
|
||||||
*/
|
*/
|
||||||
void setResolution(int width, int height) {
|
public void setResolution(int width, int height) {
|
||||||
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "width"), width);
|
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "width"), width);
|
||||||
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "height"), height);
|
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "height"), height);
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ public class MjpegServer extends VideoSink {
|
|||||||
*
|
*
|
||||||
* @param fps FPS, 0 for unspecified
|
* @param fps FPS, 0 for unspecified
|
||||||
*/
|
*/
|
||||||
void setFPS(int fps) {
|
public void setFPS(int fps) {
|
||||||
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "fps"), fps);
|
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "fps"), fps);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ public class MjpegServer extends VideoSink {
|
|||||||
*
|
*
|
||||||
* @param quality JPEG compression quality (0-100), -1 for unspecified
|
* @param quality JPEG compression quality (0-100), -1 for unspecified
|
||||||
*/
|
*/
|
||||||
void setCompression(int quality) {
|
public void setCompression(int quality) {
|
||||||
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "compression"),
|
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "compression"),
|
||||||
quality);
|
quality);
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ public class MjpegServer extends VideoSink {
|
|||||||
*
|
*
|
||||||
* @param quality JPEG compression quality (0-100)
|
* @param quality JPEG compression quality (0-100)
|
||||||
*/
|
*/
|
||||||
void setDefaultCompression(int quality) {
|
public void setDefaultCompression(int quality) {
|
||||||
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "default_compression"),
|
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "default_compression"),
|
||||||
quality);
|
quality);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,6 +133,38 @@ public class VideoSink implements AutoCloseable {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set properties from a JSON configuration string.
|
||||||
|
*
|
||||||
|
* <p>The format of the JSON input is:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "properties": [
|
||||||
|
* {
|
||||||
|
* "name": property name
|
||||||
|
* "value": property value
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param config configuration
|
||||||
|
* @return True if set successfully
|
||||||
|
*/
|
||||||
|
public boolean setConfigJson(String config) {
|
||||||
|
return CameraServerJNI.setSinkConfigJson(m_handle, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a JSON configuration string.
|
||||||
|
*
|
||||||
|
* @return JSON configuration string
|
||||||
|
*/
|
||||||
|
public String getConfigJson() {
|
||||||
|
return CameraServerJNI.getSinkConfigJson(m_handle);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure which source should provide frames to this sink. Each sink
|
* Configure which source should provide frames to this sink. Each sink
|
||||||
* can accept frames from only a single source, but a single source can
|
* can accept frames from only a single source, but a single source can
|
||||||
|
|||||||
@@ -111,13 +111,13 @@ class MjpegServerImpl::ConnThread : public wpi::SafeThread {
|
|||||||
|
|
||||||
void StartStream() {
|
void StartStream() {
|
||||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||||
m_source->EnableSink();
|
if (m_source) m_source->EnableSink();
|
||||||
m_streaming = true;
|
m_streaming = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopStream() {
|
void StopStream() {
|
||||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||||
m_source->DisableSink();
|
if (m_source) m_source->DisableSink();
|
||||||
m_streaming = false;
|
m_streaming = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,11 @@
|
|||||||
|
|
||||||
#include "PropertyContainer.h"
|
#include "PropertyContainer.h"
|
||||||
|
|
||||||
|
#include <wpi/Logger.h>
|
||||||
|
#include <wpi/SmallString.h>
|
||||||
|
#include <wpi/SmallVector.h>
|
||||||
|
#include <wpi/json.h>
|
||||||
|
|
||||||
using namespace cs;
|
using namespace cs;
|
||||||
|
|
||||||
int PropertyContainer::GetPropertyIndex(const wpi::Twine& name) const {
|
int PropertyContainer::GetPropertyIndex(const wpi::Twine& name) const {
|
||||||
@@ -204,3 +209,74 @@ bool PropertyContainer::CacheProperties(CS_Status* status) const {
|
|||||||
m_properties_cached = true;
|
m_properties_cached = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PropertyContainer::SetPropertiesJson(const wpi::json& config,
|
||||||
|
wpi::Logger& logger,
|
||||||
|
wpi::StringRef 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());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int n = GetPropertyIndex(name);
|
||||||
|
try {
|
||||||
|
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 << '\'');
|
||||||
|
SetStringProperty(n, val, status);
|
||||||
|
} else if (v.is_boolean()) {
|
||||||
|
bool val = v.get<bool>();
|
||||||
|
WPI_INFO(logger, logName << ": SetConfigJson: setting property '"
|
||||||
|
<< name << "' to " << val);
|
||||||
|
SetProperty(n, val, status);
|
||||||
|
} else {
|
||||||
|
int val = v.get<int>();
|
||||||
|
WPI_INFO(logger, logName << ": SetConfigJson: setting property '"
|
||||||
|
<< name << "' to " << val);
|
||||||
|
SetProperty(n, val, status);
|
||||||
|
}
|
||||||
|
} catch (const wpi::json::exception& e) {
|
||||||
|
WPI_WARNING(logger,
|
||||||
|
logName << ": SetConfigJson: could not read property value: "
|
||||||
|
<< e.what());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpi::json PropertyContainer::GetPropertiesJsonObject(CS_Status* status) {
|
||||||
|
wpi::json j;
|
||||||
|
wpi::SmallVector<int, 32> propVec;
|
||||||
|
for (int p : EnumerateProperties(propVec, status)) {
|
||||||
|
wpi::json prop;
|
||||||
|
wpi::SmallString<128> strBuf;
|
||||||
|
prop.emplace("name", GetPropertyName(p, strBuf, status));
|
||||||
|
switch (GetPropertyKind(p)) {
|
||||||
|
case CS_PROP_BOOLEAN:
|
||||||
|
prop.emplace("value", static_cast<bool>(GetProperty(p, status)));
|
||||||
|
break;
|
||||||
|
case CS_PROP_INTEGER:
|
||||||
|
case CS_PROP_ENUM:
|
||||||
|
prop.emplace("value", GetProperty(p, status));
|
||||||
|
break;
|
||||||
|
case CS_PROP_STRING:
|
||||||
|
prop.emplace("value", GetStringProperty(p, strBuf, status));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
j.emplace_back(prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,11 @@
|
|||||||
#include "PropertyImpl.h"
|
#include "PropertyImpl.h"
|
||||||
#include "cscore_cpp.h"
|
#include "cscore_cpp.h"
|
||||||
|
|
||||||
|
namespace wpi {
|
||||||
|
class Logger;
|
||||||
|
class json;
|
||||||
|
} // namespace wpi
|
||||||
|
|
||||||
namespace cs {
|
namespace cs {
|
||||||
|
|
||||||
class PropertyContainer {
|
class PropertyContainer {
|
||||||
@@ -50,6 +55,10 @@ class PropertyContainer {
|
|||||||
std::vector<std::string> GetEnumPropertyChoices(int property,
|
std::vector<std::string> GetEnumPropertyChoices(int property,
|
||||||
CS_Status* status) const;
|
CS_Status* status) const;
|
||||||
|
|
||||||
|
bool SetPropertiesJson(const wpi::json& config, wpi::Logger& logger,
|
||||||
|
wpi::StringRef logName, CS_Status* status);
|
||||||
|
wpi::json GetPropertiesJsonObject(CS_Status* status);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Get a property; must be called with m_mutex held.
|
// Get a property; must be called with m_mutex held.
|
||||||
PropertyImpl* GetProperty(int property) {
|
PropertyImpl* GetProperty(int property) {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#include "SinkImpl.h"
|
#include "SinkImpl.h"
|
||||||
|
|
||||||
|
#include <wpi/json.h>
|
||||||
|
|
||||||
#include "Instance.h"
|
#include "Instance.h"
|
||||||
#include "Notifier.h"
|
#include "Notifier.h"
|
||||||
#include "SourceImpl.h"
|
#include "SourceImpl.h"
|
||||||
@@ -102,6 +104,43 @@ wpi::StringRef SinkImpl::GetError(wpi::SmallVectorImpl<char>& buf) const {
|
|||||||
return wpi::StringRef{buf.data(), buf.size()};
|
return wpi::StringRef{buf.data(), buf.size()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SinkImpl::SetConfigJson(wpi::StringRef 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());
|
||||||
|
*status = CS_PROPERTY_WRITE_FAILED;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return SetConfigJson(j, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SinkImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
|
||||||
|
if (config.count("properties") != 0)
|
||||||
|
SetPropertiesJson(config.at("properties"), m_logger, GetName(), status);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SinkImpl::GetConfigJson(CS_Status* status) {
|
||||||
|
std::string rv;
|
||||||
|
wpi::raw_string_ostream os(rv);
|
||||||
|
GetConfigJsonObject(status).dump(os, 4);
|
||||||
|
os.flush();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpi::json SinkImpl::GetConfigJsonObject(CS_Status* status) {
|
||||||
|
wpi::json j;
|
||||||
|
|
||||||
|
wpi::json props = GetPropertiesJsonObject(status);
|
||||||
|
if (props.is_array()) j.emplace("properties", props);
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
void SinkImpl::NotifyPropertyCreated(int propIndex, PropertyImpl& prop) {
|
void SinkImpl::NotifyPropertyCreated(int propIndex, PropertyImpl& prop) {
|
||||||
m_notifier.NotifySinkProperty(*this, CS_SINK_PROPERTY_CREATED, prop.name,
|
m_notifier.NotifySinkProperty(*this, CS_SINK_PROPERTY_CREATED, prop.name,
|
||||||
propIndex, prop.propKind, prop.value,
|
propIndex, prop.propKind, prop.value,
|
||||||
|
|||||||
@@ -18,6 +18,10 @@
|
|||||||
|
|
||||||
#include "SourceImpl.h"
|
#include "SourceImpl.h"
|
||||||
|
|
||||||
|
namespace wpi {
|
||||||
|
class json;
|
||||||
|
} // namespace wpi
|
||||||
|
|
||||||
namespace cs {
|
namespace cs {
|
||||||
|
|
||||||
class Frame;
|
class Frame;
|
||||||
@@ -51,6 +55,11 @@ class SinkImpl : public PropertyContainer {
|
|||||||
std::string GetError() const;
|
std::string GetError() const;
|
||||||
wpi::StringRef GetError(wpi::SmallVectorImpl<char>& buf) const;
|
wpi::StringRef GetError(wpi::SmallVectorImpl<char>& buf) const;
|
||||||
|
|
||||||
|
bool SetConfigJson(wpi::StringRef 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);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// PropertyContainer implementation
|
// PropertyContainer implementation
|
||||||
void NotifyPropertyCreated(int propIndex, PropertyImpl& prop) override;
|
void NotifyPropertyCreated(int propIndex, PropertyImpl& prop) override;
|
||||||
|
|||||||
@@ -319,38 +319,8 @@ bool SourceImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// properties
|
// properties
|
||||||
if (config.count("properties") != 0) {
|
if (config.count("properties") != 0)
|
||||||
for (auto&& prop : config.at("properties")) {
|
SetPropertiesJson(config.at("properties"), m_logger, GetName(), status);
|
||||||
std::string name;
|
|
||||||
try {
|
|
||||||
name = prop.at("name").get<std::string>();
|
|
||||||
} catch (const wpi::json::exception& e) {
|
|
||||||
SWARNING("SetConfigJson: could not read property name: " << e.what());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int n = GetPropertyIndex(name);
|
|
||||||
try {
|
|
||||||
auto& v = prop.at("value");
|
|
||||||
if (v.is_string()) {
|
|
||||||
std::string val = v.get<std::string>();
|
|
||||||
SINFO("SetConfigJson: setting property '" << name << "' to '" << val
|
|
||||||
<< '\'');
|
|
||||||
SetStringProperty(n, val, status);
|
|
||||||
} else if (v.is_boolean()) {
|
|
||||||
bool val = v.get<bool>();
|
|
||||||
SINFO("SetConfigJson: setting property '" << name << "' to " << val);
|
|
||||||
SetProperty(n, val, status);
|
|
||||||
} else {
|
|
||||||
int val = v.get<int>();
|
|
||||||
SINFO("SetConfigJson: setting property '" << name << "' to " << val);
|
|
||||||
SetProperty(n, val, status);
|
|
||||||
}
|
|
||||||
} catch (const wpi::json::exception& e) {
|
|
||||||
SWARNING("SetConfigJson: could not read property value: " << e.what());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -401,28 +371,7 @@ wpi::json SourceImpl::GetConfigJsonObject(CS_Status* status) {
|
|||||||
// TODO: output brightness, white balance, and exposure?
|
// TODO: output brightness, white balance, and exposure?
|
||||||
|
|
||||||
// properties
|
// properties
|
||||||
wpi::json props;
|
wpi::json props = GetPropertiesJsonObject(status);
|
||||||
wpi::SmallVector<int, 32> propVec;
|
|
||||||
for (int p : EnumerateProperties(propVec, status)) {
|
|
||||||
wpi::json prop;
|
|
||||||
wpi::SmallString<128> strBuf;
|
|
||||||
prop.emplace("name", GetPropertyName(p, strBuf, status));
|
|
||||||
switch (GetPropertyKind(p)) {
|
|
||||||
case CS_PROP_BOOLEAN:
|
|
||||||
prop.emplace("value", static_cast<bool>(GetProperty(p, status)));
|
|
||||||
break;
|
|
||||||
case CS_PROP_INTEGER:
|
|
||||||
case CS_PROP_ENUM:
|
|
||||||
prop.emplace("value", GetProperty(p, status));
|
|
||||||
break;
|
|
||||||
case CS_PROP_STRING:
|
|
||||||
prop.emplace("value", GetStringProperty(p, strBuf, status));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
props.emplace_back(prop);
|
|
||||||
}
|
|
||||||
if (props.is_array()) j.emplace("properties", props);
|
if (props.is_array()) j.emplace("properties", props);
|
||||||
|
|
||||||
return j;
|
return j;
|
||||||
|
|||||||
@@ -277,6 +277,15 @@ CS_Property* CS_EnumerateSinkProperties(CS_Sink sink, int* count,
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CS_Bool CS_SetSinkConfigJson(CS_Sink sink, const char* config,
|
||||||
|
CS_Status* status) {
|
||||||
|
return cs::SetSinkConfigJson(sink, config, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* CS_GetSinkConfigJson(CS_Sink sink, CS_Status* status) {
|
||||||
|
return cs::ConvertToC(cs::GetSinkConfigJson(sink, status));
|
||||||
|
}
|
||||||
|
|
||||||
void CS_SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status) {
|
void CS_SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status) {
|
||||||
return cs::SetSinkSource(sink, source, status);
|
return cs::SetSinkSource(sink, source, status);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -564,6 +564,43 @@ wpi::ArrayRef<CS_Property> EnumerateSinkProperties(
|
|||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SetSinkConfigJson(CS_Sink sink, wpi::StringRef config, CS_Status* status) {
|
||||||
|
auto data = Instance::GetInstance().GetSink(sink);
|
||||||
|
if (!data) {
|
||||||
|
*status = CS_INVALID_HANDLE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return data->sink->SetConfigJson(config, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetSinkConfigJson(CS_Sink sink, const wpi::json& config,
|
||||||
|
CS_Status* status) {
|
||||||
|
auto data = Instance::GetInstance().GetSink(sink);
|
||||||
|
if (!data) {
|
||||||
|
*status = CS_INVALID_HANDLE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return data->sink->SetConfigJson(config, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetSinkConfigJson(CS_Sink sink, CS_Status* status) {
|
||||||
|
auto data = Instance::GetInstance().GetSink(sink);
|
||||||
|
if (!data) {
|
||||||
|
*status = CS_INVALID_HANDLE;
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
return data->sink->GetConfigJson(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
wpi::json GetSinkConfigJsonObject(CS_Sink sink, CS_Status* status) {
|
||||||
|
auto data = Instance::GetInstance().GetSink(sink);
|
||||||
|
if (!data) {
|
||||||
|
*status = CS_INVALID_HANDLE;
|
||||||
|
return wpi::json{};
|
||||||
|
}
|
||||||
|
return data->sink->GetConfigJsonObject(status);
|
||||||
|
}
|
||||||
|
|
||||||
void SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status) {
|
void SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status) {
|
||||||
auto data = Instance::GetInstance().GetSink(sink);
|
auto data = Instance::GetInstance().GetSink(sink);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ wpi::json VideoSource::GetConfigJsonObject() const {
|
|||||||
return GetSourceConfigJsonObject(m_handle, &m_status);
|
return GetSourceConfigJsonObject(m_handle, &m_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wpi::json VideoSink::GetConfigJsonObject() const {
|
||||||
|
m_status = 0;
|
||||||
|
return GetSinkConfigJsonObject(m_handle, &m_status);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<VideoProperty> VideoSource::EnumerateProperties() const {
|
std::vector<VideoProperty> VideoSource::EnumerateProperties() const {
|
||||||
wpi::SmallVector<CS_Property, 32> handles_buf;
|
wpi::SmallVector<CS_Property, 32> handles_buf;
|
||||||
CS_Status status = 0;
|
CS_Status status = 0;
|
||||||
|
|||||||
@@ -1292,6 +1292,36 @@ Java_edu_wpi_cscore_CameraServerJNI_enumerateSinkProperties
|
|||||||
return MakeJIntArray(env, arr);
|
return MakeJIntArray(env, arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_cscore_CameraServerJNI
|
||||||
|
* Method: setSinkConfigJson
|
||||||
|
* Signature: (ILjava/lang/String;)Z
|
||||||
|
*/
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_edu_wpi_cscore_CameraServerJNI_setSinkConfigJson
|
||||||
|
(JNIEnv* env, jclass, jint source, jstring config)
|
||||||
|
{
|
||||||
|
CS_Status status = 0;
|
||||||
|
auto val = cs::SetSinkConfigJson(source, JStringRef{env, config}, &status);
|
||||||
|
CheckStatus(env, status);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_cscore_CameraServerJNI
|
||||||
|
* Method: getSinkConfigJson
|
||||||
|
* Signature: (I)Ljava/lang/String;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jstring JNICALL
|
||||||
|
Java_edu_wpi_cscore_CameraServerJNI_getSinkConfigJson
|
||||||
|
(JNIEnv* env, jclass, jint source)
|
||||||
|
{
|
||||||
|
CS_Status status = 0;
|
||||||
|
auto val = cs::GetSinkConfigJson(source, &status);
|
||||||
|
CheckStatus(env, status);
|
||||||
|
return MakeJString(env, val);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: edu_wpi_cscore_CameraServerJNI
|
* Class: edu_wpi_cscore_CameraServerJNI
|
||||||
* Method: setSinkSource
|
* Method: setSinkSource
|
||||||
|
|||||||
@@ -393,6 +393,9 @@ CS_Property* CS_EnumerateSinkProperties(CS_Sink sink, int* count,
|
|||||||
void CS_SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status);
|
void CS_SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status);
|
||||||
CS_Property CS_GetSinkSourceProperty(CS_Sink sink, const char* name,
|
CS_Property CS_GetSinkSourceProperty(CS_Sink sink, const char* name,
|
||||||
CS_Status* status);
|
CS_Status* status);
|
||||||
|
CS_Bool CS_SetSinkConfigJson(CS_Sink sink, const char* config,
|
||||||
|
CS_Status* status);
|
||||||
|
char* CS_GetSinkConfigJson(CS_Sink sink, CS_Status* status);
|
||||||
CS_Source CS_GetSinkSource(CS_Sink sink, CS_Status* status);
|
CS_Source CS_GetSinkSource(CS_Sink sink, CS_Status* status);
|
||||||
CS_Sink CS_CopySink(CS_Sink sink, CS_Status* status);
|
CS_Sink CS_CopySink(CS_Sink sink, CS_Status* status);
|
||||||
void CS_ReleaseSink(CS_Sink sink, CS_Status* status);
|
void CS_ReleaseSink(CS_Sink sink, CS_Status* status);
|
||||||
|
|||||||
@@ -332,6 +332,11 @@ wpi::ArrayRef<CS_Property> EnumerateSinkProperties(
|
|||||||
void SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status);
|
void SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status);
|
||||||
CS_Property GetSinkSourceProperty(CS_Sink sink, const wpi::Twine& name,
|
CS_Property GetSinkSourceProperty(CS_Sink sink, const wpi::Twine& name,
|
||||||
CS_Status* status);
|
CS_Status* status);
|
||||||
|
bool SetSinkConfigJson(CS_Sink sink, wpi::StringRef config, CS_Status* status);
|
||||||
|
bool SetSinkConfigJson(CS_Sink sink, const wpi::json& config,
|
||||||
|
CS_Status* status);
|
||||||
|
std::string GetSinkConfigJson(CS_Sink sink, CS_Status* status);
|
||||||
|
wpi::json GetSinkConfigJsonObject(CS_Sink sink, CS_Status* status);
|
||||||
CS_Source GetSinkSource(CS_Sink sink, CS_Status* status);
|
CS_Source GetSinkSource(CS_Sink sink, CS_Status* status);
|
||||||
CS_Sink CopySink(CS_Sink sink, CS_Status* status);
|
CS_Sink CopySink(CS_Sink sink, CS_Status* status);
|
||||||
void ReleaseSink(CS_Sink sink, CS_Status* status);
|
void ReleaseSink(CS_Sink sink, CS_Status* status);
|
||||||
|
|||||||
@@ -807,6 +807,49 @@ class VideoSink {
|
|||||||
*/
|
*/
|
||||||
std::vector<VideoProperty> EnumerateProperties() const;
|
std::vector<VideoProperty> EnumerateProperties() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set properties from a JSON configuration string.
|
||||||
|
*
|
||||||
|
* The format of the JSON input is:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "properties": [
|
||||||
|
* {
|
||||||
|
* "name": property name
|
||||||
|
* "value": property value
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param config configuration
|
||||||
|
* @return True if set successfully
|
||||||
|
*/
|
||||||
|
bool SetConfigJson(wpi::StringRef config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set properties from a JSON configuration object.
|
||||||
|
*
|
||||||
|
* @param config configuration
|
||||||
|
* @return True if set successfully
|
||||||
|
*/
|
||||||
|
bool SetConfigJson(const wpi::json& config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a JSON configuration string.
|
||||||
|
*
|
||||||
|
* @return JSON configuration string
|
||||||
|
*/
|
||||||
|
std::string GetConfigJson() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a JSON configuration object.
|
||||||
|
*
|
||||||
|
* @return JSON configuration object
|
||||||
|
*/
|
||||||
|
wpi::json GetConfigJsonObject() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure which source should provide frames to this sink. Each sink
|
* Configure which source should provide frames to this sink. Each sink
|
||||||
* can accept frames from only a single source, but a single source can
|
* can accept frames from only a single source, but a single source can
|
||||||
|
|||||||
@@ -521,6 +521,21 @@ inline VideoProperty VideoSink::GetSourceProperty(const wpi::Twine& name) {
|
|||||||
return VideoProperty{GetSinkSourceProperty(m_handle, name, &m_status)};
|
return VideoProperty{GetSinkSourceProperty(m_handle, name, &m_status)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool VideoSink::SetConfigJson(wpi::StringRef config) {
|
||||||
|
m_status = 0;
|
||||||
|
return SetSinkConfigJson(m_handle, config, &m_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool VideoSink::SetConfigJson(const wpi::json& config) {
|
||||||
|
m_status = 0;
|
||||||
|
return SetSinkConfigJson(m_handle, config, &m_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string VideoSink::GetConfigJson() const {
|
||||||
|
m_status = 0;
|
||||||
|
return GetSinkConfigJson(m_handle, &m_status);
|
||||||
|
}
|
||||||
|
|
||||||
inline MjpegServer::MjpegServer(const wpi::Twine& name,
|
inline MjpegServer::MjpegServer(const wpi::Twine& name,
|
||||||
const wpi::Twine& listenAddress, int port) {
|
const wpi::Twine& listenAddress, int port) {
|
||||||
m_handle = CreateMjpegServer(name, listenAddress, port, &m_status);
|
m_handle = CreateMjpegServer(name, listenAddress, port, &m_status);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <wpi/STLExtras.h>
|
#include <wpi/STLExtras.h>
|
||||||
#include <wpi/SmallString.h>
|
#include <wpi/SmallString.h>
|
||||||
|
#include <wpi/raw_ostream.h>
|
||||||
|
|
||||||
#include "UsbUtil.h"
|
#include "UsbUtil.h"
|
||||||
|
|
||||||
@@ -151,6 +152,9 @@ UsbCameraProperty::UsbCameraProperty(const struct v4l2_query_ext_ctrl& ctrl)
|
|||||||
propKind = CS_PROP_BOOLEAN;
|
propKind = CS_PROP_BOOLEAN;
|
||||||
break;
|
break;
|
||||||
case V4L2_CTRL_TYPE_INTEGER_MENU:
|
case V4L2_CTRL_TYPE_INTEGER_MENU:
|
||||||
|
propKind = CS_PROP_ENUM;
|
||||||
|
intMenu = true;
|
||||||
|
break;
|
||||||
case V4L2_CTRL_TYPE_MENU:
|
case V4L2_CTRL_TYPE_MENU:
|
||||||
propKind = CS_PROP_ENUM;
|
propKind = CS_PROP_ENUM;
|
||||||
break;
|
break;
|
||||||
@@ -243,7 +247,12 @@ std::unique_ptr<UsbCameraProperty> UsbCameraProperty::DeviceQuery(int fd,
|
|||||||
for (int i = prop->minimum; i <= prop->maximum; ++i) {
|
for (int i = prop->minimum; i <= prop->maximum; ++i) {
|
||||||
qmenu.index = static_cast<__u32>(i);
|
qmenu.index = static_cast<__u32>(i);
|
||||||
if (TryIoctl(fd, VIDIOC_QUERYMENU, &qmenu) != 0) continue;
|
if (TryIoctl(fd, VIDIOC_QUERYMENU, &qmenu) != 0) continue;
|
||||||
prop->enumChoices[i] = reinterpret_cast<const char*>(qmenu.name);
|
if (prop->intMenu) {
|
||||||
|
wpi::raw_string_ostream os(prop->enumChoices[i]);
|
||||||
|
os << qmenu.value;
|
||||||
|
} else {
|
||||||
|
prop->enumChoices[i] = reinterpret_cast<const char*>(qmenu.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ class UsbCameraProperty : public PropertyImpl {
|
|||||||
|
|
||||||
unsigned id{0}; // implementation-level id
|
unsigned id{0}; // implementation-level id
|
||||||
int type{0}; // implementation type, not CS_PropertyKind!
|
int type{0}; // implementation type, not CS_PropertyKind!
|
||||||
|
|
||||||
|
// If the enum property is integer rather than string
|
||||||
|
bool intMenu{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cs
|
} // namespace cs
|
||||||
|
|||||||
@@ -5,27 +5,12 @@
|
|||||||
/* the project. */
|
/* the project. */
|
||||||
/*----------------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
#include <winsock2.h> // NOLINT(build/include_order)
|
|
||||||
|
|
||||||
#include <iphlpapi.h> // NOLINT(build/include_order)
|
|
||||||
|
|
||||||
#include <ws2tcpip.h> // NOLINT(build/include_order)
|
|
||||||
|
|
||||||
#include <uv.h>
|
#include <uv.h>
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "cscore_cpp.h"
|
#include "cscore_cpp.h"
|
||||||
|
|
||||||
#pragma comment(lib, "IPHLPAPI.lib")
|
|
||||||
#pragma comment(lib, "Ws2_32.lib")
|
#pragma comment(lib, "Ws2_32.lib")
|
||||||
|
|
||||||
#define WORKING_BUFFER_SIZE 15000
|
|
||||||
#define MAX_TRIES 3
|
|
||||||
|
|
||||||
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
|
|
||||||
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
|
|
||||||
|
|
||||||
namespace cs {
|
namespace cs {
|
||||||
|
|
||||||
std::vector<std::string> GetNetworkInterfaces() {
|
std::vector<std::string> GetNetworkInterfaces() {
|
||||||
@@ -40,80 +25,18 @@ std::vector<std::string> GetNetworkInterfaces() {
|
|||||||
|
|
||||||
for (int i = 0; i < counts; i++) {
|
for (int i = 0; i < counts; i++) {
|
||||||
if (adrs[i].is_internal) continue;
|
if (adrs[i].is_internal) continue;
|
||||||
std::cout << adrs[i].name << std::endl;
|
|
||||||
InetNtop(PF_INET, &(adrs[i].netmask.netmask4.sin_addr.s_addr), ip,
|
InetNtop(PF_INET, &(adrs[i].netmask.netmask4.sin_addr.s_addr), ip,
|
||||||
sizeof(ip) - 1);
|
sizeof(ip) - 1);
|
||||||
ip[49] = '\0';
|
ip[49] = '\0';
|
||||||
std::cout << ip << std::endl;
|
|
||||||
InetNtop(PF_INET, &(adrs[i].address.address4.sin_addr.s_addr), ip,
|
InetNtop(PF_INET, &(adrs[i].address.address4.sin_addr.s_addr), ip,
|
||||||
sizeof(ip) - 1);
|
sizeof(ip) - 1);
|
||||||
ip[49] = '\0';
|
ip[49] = '\0';
|
||||||
std::cout << ip << std::endl;
|
|
||||||
addresses.emplace_back(std::string{ip});
|
addresses.emplace_back(std::string{ip});
|
||||||
}
|
}
|
||||||
|
|
||||||
uv_free_interface_addresses(adrs, counts);
|
uv_free_interface_addresses(adrs, counts);
|
||||||
|
|
||||||
std::cout << "finished\n";
|
|
||||||
|
|
||||||
return addresses;
|
return addresses;
|
||||||
|
|
||||||
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
|
|
||||||
PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
|
|
||||||
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
|
|
||||||
PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;
|
|
||||||
PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;
|
|
||||||
unsigned int i = 0;
|
|
||||||
|
|
||||||
ULONG outBufLen = 0;
|
|
||||||
ULONG Iterations = 0;
|
|
||||||
DWORD dwRetVal = 0;
|
|
||||||
|
|
||||||
// Allocate a 15 KB buffer to start with.
|
|
||||||
outBufLen = WORKING_BUFFER_SIZE;
|
|
||||||
|
|
||||||
do {
|
|
||||||
pAddresses = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(MALLOC(outBufLen));
|
|
||||||
if (pAddresses == NULL) {
|
|
||||||
std::printf("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
|
|
||||||
std::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
dwRetVal = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL,
|
|
||||||
pAddresses, &outBufLen);
|
|
||||||
|
|
||||||
if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
|
|
||||||
FREE(pAddresses);
|
|
||||||
pAddresses = NULL;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterations++;
|
|
||||||
} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));
|
|
||||||
|
|
||||||
if (dwRetVal == NO_ERROR) {
|
|
||||||
pCurrAddresses = pAddresses;
|
|
||||||
while (pCurrAddresses) {
|
|
||||||
pUnicast = pCurrAddresses->FirstUnicastAddress;
|
|
||||||
while (pUnicast != NULL) {
|
|
||||||
sockaddr_in* address =
|
|
||||||
reinterpret_cast<sockaddr_in*>(pUnicast->Address.lpSockaddr);
|
|
||||||
InetNtop(PF_INET, &(address->sin_addr.s_addr), ip, sizeof(ip) - 1);
|
|
||||||
ip[49] = '\0';
|
|
||||||
addresses.emplace_back(std::string{ip});
|
|
||||||
pUnicast = pUnicast->Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
pCurrAddresses = pCurrAddresses->Next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pAddresses) {
|
|
||||||
FREE(pAddresses);
|
|
||||||
}
|
|
||||||
|
|
||||||
return addresses; // TODO
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace cs
|
} // namespace cs
|
||||||
|
|||||||
@@ -450,6 +450,15 @@ bool UsbCameraImpl::DeviceConnect() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CS_Status st = 0;
|
||||||
|
auto devices = EnumerateUsbCameras(&st);
|
||||||
|
|
||||||
|
for (auto&& device : devices) {
|
||||||
|
if (device.path == m_path) {
|
||||||
|
SetDescription(device.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_properties_cached) {
|
if (!m_properties_cached) {
|
||||||
SDEBUG3("caching properties");
|
SDEBUG3("caching properties");
|
||||||
DeviceCacheProperties();
|
DeviceCacheProperties();
|
||||||
@@ -488,21 +497,32 @@ bool UsbCameraImpl::CacheProperties(CS_Status* status) const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UsbCameraImpl::DeviceAddProperty(const wpi::Twine& name_,
|
template <typename TagProperty, typename IAM>
|
||||||
tagVideoProcAmpProperty tag,
|
void UsbCameraImpl::DeviceAddProperty(const wpi::Twine& name_, TagProperty tag,
|
||||||
IAMVideoProcAmp* pProcAmp) {
|
IAM* pProcAmp) {
|
||||||
// First see if properties exist
|
// First see if properties exist
|
||||||
bool isValid = false;
|
bool isValid = false;
|
||||||
auto property = std::make_unique<UsbCameraProperty>(name_, tag, false,
|
auto property = std::make_unique<UsbCameraProperty>(name_, tag, false,
|
||||||
pProcAmp, &isValid);
|
pProcAmp, &isValid);
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
DeviceCacheProperty(std::move(property), pProcAmp);
|
DeviceCacheProperty(std::move(property), m_sourceReader.Get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template void UsbCameraImpl::DeviceAddProperty(const wpi::Twine& name_,
|
||||||
|
tagVideoProcAmpProperty tag,
|
||||||
|
IAMVideoProcAmp* pProcAmp);
|
||||||
|
|
||||||
|
template void UsbCameraImpl::DeviceAddProperty(const wpi::Twine& name_,
|
||||||
|
tagCameraControlProperty tag,
|
||||||
|
IAMCameraControl* pProcAmp);
|
||||||
|
|
||||||
#define CREATEPROPERTY(val) \
|
#define CREATEPROPERTY(val) \
|
||||||
DeviceAddProperty(#val, VideoProcAmp_##val, pProcAmp);
|
DeviceAddProperty(#val, VideoProcAmp_##val, pProcAmp);
|
||||||
|
|
||||||
|
#define CREATECONTROLPROPERTY(val) \
|
||||||
|
DeviceAddProperty(#val, CameraControl_##val, pCamControl);
|
||||||
|
|
||||||
void UsbCameraImpl::DeviceCacheProperties() {
|
void UsbCameraImpl::DeviceCacheProperties() {
|
||||||
if (!m_sourceReader) return;
|
if (!m_sourceReader) return;
|
||||||
|
|
||||||
@@ -523,6 +543,21 @@ void UsbCameraImpl::DeviceCacheProperties() {
|
|||||||
CREATEPROPERTY(Gain)
|
CREATEPROPERTY(Gain)
|
||||||
pProcAmp->Release();
|
pProcAmp->Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IAMCameraControl* pCamControl = NULL;
|
||||||
|
|
||||||
|
if (SUCCEEDED(m_sourceReader->GetServiceForStream(
|
||||||
|
(DWORD)MF_SOURCE_READER_MEDIASOURCE, GUID_NULL,
|
||||||
|
IID_PPV_ARGS(&pCamControl)))) {
|
||||||
|
CREATECONTROLPROPERTY(Pan)
|
||||||
|
CREATECONTROLPROPERTY(Tilt)
|
||||||
|
CREATECONTROLPROPERTY(Roll)
|
||||||
|
CREATECONTROLPROPERTY(Zoom)
|
||||||
|
CREATECONTROLPROPERTY(Exposure)
|
||||||
|
CREATECONTROLPROPERTY(Iris)
|
||||||
|
CREATECONTROLPROPERTY(Focus)
|
||||||
|
pCamControl->Release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int UsbCameraImpl::RawToPercentage(const UsbCameraProperty& rawProp,
|
int UsbCameraImpl::RawToPercentage(const UsbCameraProperty& rawProp,
|
||||||
@@ -538,7 +573,7 @@ int UsbCameraImpl::PercentageToRaw(const UsbCameraProperty& rawProp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UsbCameraImpl::DeviceCacheProperty(
|
void UsbCameraImpl::DeviceCacheProperty(
|
||||||
std::unique_ptr<UsbCameraProperty> rawProp, IAMVideoProcAmp* pProcAmp) {
|
std::unique_ptr<UsbCameraProperty> rawProp, IMFSourceReader* pProcAmp) {
|
||||||
// For percentage properties, we want to cache both the raw and the
|
// For percentage properties, we want to cache both the raw and the
|
||||||
// percentage versions. This function is always called with prop being
|
// percentage versions. This function is always called with prop being
|
||||||
// the raw property (as it's coming from the camera) so if required, we need
|
// the raw property (as it's coming from the camera) so if required, we need
|
||||||
@@ -707,16 +742,7 @@ CS_StatusValue UsbCameraImpl::DeviceCmdSetProperty(
|
|||||||
if (!prop->device) {
|
if (!prop->device) {
|
||||||
if (prop->id == kPropConnectVerboseId) m_connectVerbose = value;
|
if (prop->id == kPropConnectVerboseId) m_connectVerbose = value;
|
||||||
} else {
|
} else {
|
||||||
IAMVideoProcAmp* pProcAmp = NULL;
|
if (!prop->DeviceSet(lock, m_sourceReader.Get())) {
|
||||||
if (SUCCEEDED(m_sourceReader->GetServiceForStream(
|
|
||||||
(DWORD)MF_SOURCE_READER_MEDIASOURCE, GUID_NULL,
|
|
||||||
IID_PPV_ARGS(&pProcAmp)))) {
|
|
||||||
if (!prop->DeviceSet(lock, pProcAmp, value)) {
|
|
||||||
pProcAmp->Release();
|
|
||||||
return CS_PROPERTY_WRITE_FAILED;
|
|
||||||
}
|
|
||||||
pProcAmp->Release();
|
|
||||||
} else {
|
|
||||||
return CS_PROPERTY_WRITE_FAILED;
|
return CS_PROPERTY_WRITE_FAILED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -933,9 +959,7 @@ std::vector<UsbCameraInfo> EnumerateUsbCameras(CS_Status* status) {
|
|||||||
// Ensure we are initialized by grabbing the message pump
|
// Ensure we are initialized by grabbing the message pump
|
||||||
// GetMessagePump();
|
// GetMessagePump();
|
||||||
|
|
||||||
ComPtr<IMFMediaSource> ppSource;
|
|
||||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
|
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
|
||||||
ComPtr<IMFMediaSource> pSource;
|
|
||||||
ComPtr<IMFAttributes> pAttributes;
|
ComPtr<IMFAttributes> pAttributes;
|
||||||
IMFActivate** ppDevices = nullptr;
|
IMFActivate** ppDevices = nullptr;
|
||||||
|
|
||||||
@@ -988,7 +1012,6 @@ done:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CoTaskMemFree(ppDevices);
|
CoTaskMemFree(ppDevices);
|
||||||
pSource.Reset();
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------------*/
|
||||||
/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
|
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||||
/* the project. */
|
/* the project. */
|
||||||
@@ -132,11 +132,12 @@ class UsbCameraImpl : public SourceImpl,
|
|||||||
CS_StatusValue DeviceSetMode();
|
CS_StatusValue DeviceSetMode();
|
||||||
void DeviceCacheMode();
|
void DeviceCacheMode();
|
||||||
void DeviceCacheProperty(std::unique_ptr<UsbCameraProperty> rawProp,
|
void DeviceCacheProperty(std::unique_ptr<UsbCameraProperty> rawProp,
|
||||||
IAMVideoProcAmp* pProcAmp);
|
IMFSourceReader* sourceReader);
|
||||||
void DeviceCacheProperties();
|
void DeviceCacheProperties();
|
||||||
void DeviceCacheVideoModes();
|
void DeviceCacheVideoModes();
|
||||||
void DeviceAddProperty(const wpi::Twine& name_, tagVideoProcAmpProperty tag,
|
template <typename TagProperty, typename IAM>
|
||||||
IAMVideoProcAmp* pProcAmp);
|
void DeviceAddProperty(const wpi::Twine& name_, TagProperty tag,
|
||||||
|
IAM* pProcAmp);
|
||||||
|
|
||||||
ComPtr<IMFMediaType> DeviceCheckModeValid(const VideoMode& toCheck);
|
ComPtr<IMFMediaType> DeviceCheckModeValid(const VideoMode& toCheck);
|
||||||
|
|
||||||
|
|||||||
@@ -7,13 +7,16 @@
|
|||||||
|
|
||||||
#include "UsbCameraProperty.h"
|
#include "UsbCameraProperty.h"
|
||||||
|
|
||||||
|
#include "ComPtr.h"
|
||||||
|
|
||||||
using namespace cs;
|
using namespace cs;
|
||||||
|
|
||||||
UsbCameraProperty::UsbCameraProperty(const wpi::Twine& name_,
|
UsbCameraProperty::UsbCameraProperty(const wpi::Twine& name_,
|
||||||
tagVideoProcAmpProperty tag, bool autoProp,
|
tagVideoProcAmpProperty tag, bool autoProp,
|
||||||
IAMVideoProcAmp* pProcAmp, bool* isValid)
|
IAMVideoProcAmp* pProcAmp, bool* isValid)
|
||||||
: PropertyImpl{autoProp ? name_ + "_auto" : name_} {
|
: PropertyImpl{autoProp ? name_ + "_auto" : name_} {
|
||||||
this->tag = tag;
|
this->tagVideoProc = tag;
|
||||||
|
this->isControlProperty = false;
|
||||||
this->isAutoProp = autoProp;
|
this->isAutoProp = autoProp;
|
||||||
long paramVal, paramFlag; // NOLINT(runtime/int)
|
long paramVal, paramFlag; // NOLINT(runtime/int)
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
@@ -42,7 +45,7 @@ bool UsbCameraProperty::DeviceGet(std::unique_lock<wpi::mutex>& lock,
|
|||||||
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
long newValue = 0, paramFlag = 0; // NOLINT(runtime/int)
|
long newValue = 0, paramFlag = 0; // NOLINT(runtime/int)
|
||||||
if (SUCCEEDED(pProcAmp->Get(tag, &newValue, ¶mFlag))) {
|
if (SUCCEEDED(pProcAmp->Get(tagVideoProc, &newValue, ¶mFlag))) {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
value = newValue;
|
value = newValue;
|
||||||
return true;
|
return true;
|
||||||
@@ -60,10 +63,127 @@ bool UsbCameraProperty::DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
|||||||
if (!pProcAmp) return true;
|
if (!pProcAmp) return true;
|
||||||
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
if (SUCCEEDED(pProcAmp->Set(tag, newValue, VideoProcAmp_Flags_Manual))) {
|
if (SUCCEEDED(
|
||||||
|
pProcAmp->Set(tagVideoProc, newValue, VideoProcAmp_Flags_Manual))) {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UsbCameraProperty::UsbCameraProperty(const wpi::Twine& name_,
|
||||||
|
tagCameraControlProperty tag,
|
||||||
|
bool autoProp, IAMCameraControl* pProcAmp,
|
||||||
|
bool* isValid)
|
||||||
|
: PropertyImpl{autoProp ? name_ + "_auto" : name_} {
|
||||||
|
this->tagCameraControl = tag;
|
||||||
|
this->isControlProperty = true;
|
||||||
|
this->isAutoProp = autoProp;
|
||||||
|
long paramVal, paramFlag; // NOLINT(runtime/int)
|
||||||
|
HRESULT hr;
|
||||||
|
long minVal, maxVal, stepVal; // NOLINT(runtime/int)
|
||||||
|
hr = pProcAmp->GetRange(tag, &minVal, &maxVal, &stepVal, ¶mVal,
|
||||||
|
¶mFlag); // Unable to get the property, trying to
|
||||||
|
// return default value
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
minimum = minVal;
|
||||||
|
maximum = maxVal;
|
||||||
|
hasMaximum = true;
|
||||||
|
hasMinimum = true;
|
||||||
|
defaultValue = paramVal;
|
||||||
|
step = stepVal;
|
||||||
|
value = paramVal;
|
||||||
|
propKind = CS_PropertyKind::CS_PROP_INTEGER;
|
||||||
|
*isValid = true;
|
||||||
|
} else {
|
||||||
|
*isValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UsbCameraProperty::DeviceGet(std::unique_lock<wpi::mutex>& lock,
|
||||||
|
IAMCameraControl* pProcAmp) {
|
||||||
|
if (!pProcAmp) return true;
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
long newValue = 0, paramFlag = 0; // NOLINT(runtime/int)
|
||||||
|
if (SUCCEEDED(pProcAmp->Get(tagCameraControl, &newValue, ¶mFlag))) {
|
||||||
|
lock.lock();
|
||||||
|
value = newValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool UsbCameraProperty::DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
||||||
|
IAMCameraControl* pProcAmp) const {
|
||||||
|
return DeviceSet(lock, pProcAmp, value);
|
||||||
|
}
|
||||||
|
bool UsbCameraProperty::DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
||||||
|
IAMCameraControl* pProcAmp,
|
||||||
|
int newValue) const {
|
||||||
|
if (!pProcAmp) return true;
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
if (SUCCEEDED(pProcAmp->Set(tagCameraControl, newValue,
|
||||||
|
CameraControl_Flags_Manual))) {
|
||||||
|
lock.lock();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UsbCameraProperty::DeviceGet(std::unique_lock<wpi::mutex>& lock,
|
||||||
|
IMFSourceReader* sourceReader) {
|
||||||
|
if (!sourceReader) return true;
|
||||||
|
|
||||||
|
if (isControlProperty) {
|
||||||
|
ComPtr<IAMCameraControl> pProcAmp;
|
||||||
|
if (SUCCEEDED(sourceReader->GetServiceForStream(
|
||||||
|
(DWORD)MF_SOURCE_READER_MEDIASOURCE, GUID_NULL,
|
||||||
|
IID_PPV_ARGS(pProcAmp.GetAddressOf())))) {
|
||||||
|
return DeviceGet(lock, pProcAmp.Get());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ComPtr<IAMVideoProcAmp> pProcAmp;
|
||||||
|
if (SUCCEEDED(sourceReader->GetServiceForStream(
|
||||||
|
(DWORD)MF_SOURCE_READER_MEDIASOURCE, GUID_NULL,
|
||||||
|
IID_PPV_ARGS(pProcAmp.GetAddressOf())))) {
|
||||||
|
return DeviceGet(lock, pProcAmp.Get());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool UsbCameraProperty::DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
||||||
|
IMFSourceReader* sourceReader) const {
|
||||||
|
return DeviceSet(lock, sourceReader, value);
|
||||||
|
}
|
||||||
|
bool UsbCameraProperty::DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
||||||
|
IMFSourceReader* sourceReader,
|
||||||
|
int newValue) const {
|
||||||
|
if (!sourceReader) return true;
|
||||||
|
|
||||||
|
if (isControlProperty) {
|
||||||
|
ComPtr<IAMCameraControl> pProcAmp;
|
||||||
|
if (SUCCEEDED(sourceReader->GetServiceForStream(
|
||||||
|
(DWORD)MF_SOURCE_READER_MEDIASOURCE, GUID_NULL,
|
||||||
|
IID_PPV_ARGS(pProcAmp.GetAddressOf())))) {
|
||||||
|
return DeviceSet(lock, pProcAmp.Get(), newValue);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ComPtr<IAMVideoProcAmp> pProcAmp;
|
||||||
|
if (SUCCEEDED(sourceReader->GetServiceForStream(
|
||||||
|
(DWORD)MF_SOURCE_READER_MEDIASOURCE, GUID_NULL,
|
||||||
|
IID_PPV_ARGS(pProcAmp.GetAddressOf())))) {
|
||||||
|
return DeviceSet(lock, pProcAmp.Get(), newValue);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <mfapi.h>
|
||||||
|
#include <mfidl.h>
|
||||||
|
#include <mfreadwrite.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <Dshow.h>
|
#include <Dshow.h>
|
||||||
@@ -49,16 +53,23 @@ class UsbCameraProperty : public PropertyImpl {
|
|||||||
UsbCameraProperty(const wpi::Twine& name_, tagVideoProcAmpProperty tag,
|
UsbCameraProperty(const wpi::Twine& name_, tagVideoProcAmpProperty tag,
|
||||||
bool autoProp, IAMVideoProcAmp* pProcAmp, bool* isValid);
|
bool autoProp, IAMVideoProcAmp* pProcAmp, bool* isValid);
|
||||||
|
|
||||||
bool DeviceGet(std::unique_lock<wpi::mutex>& lock, IAMVideoProcAmp* pProcAmp);
|
UsbCameraProperty(const wpi::Twine& name_, tagCameraControlProperty tag,
|
||||||
|
bool autoProp, IAMCameraControl* pProcAmp, bool* isValid);
|
||||||
|
|
||||||
|
bool DeviceGet(std::unique_lock<wpi::mutex>& lock,
|
||||||
|
IMFSourceReader* sourceReader);
|
||||||
bool DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
bool DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
||||||
IAMVideoProcAmp* pProcAmp) const;
|
IMFSourceReader* sourceReader) const;
|
||||||
bool DeviceSet(std::unique_lock<wpi::mutex>& lock, IAMVideoProcAmp* pProcAmp,
|
bool DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
||||||
int newValue) const;
|
IMFSourceReader* sourceReader, int newValue) const;
|
||||||
|
|
||||||
// If this is a device (rather than software) property
|
// If this is a device (rather than software) property
|
||||||
bool device{true};
|
bool device{true};
|
||||||
bool isAutoProp{true};
|
bool isAutoProp{true};
|
||||||
tagVideoProcAmpProperty tag;
|
|
||||||
|
bool isControlProperty{false};
|
||||||
|
tagVideoProcAmpProperty tagVideoProc;
|
||||||
|
tagCameraControlProperty tagCameraControl;
|
||||||
|
|
||||||
// If this is a percentage (rather than raw) property
|
// If this is a percentage (rather than raw) property
|
||||||
bool percentage{false};
|
bool percentage{false};
|
||||||
@@ -68,5 +79,19 @@ class UsbCameraProperty : public PropertyImpl {
|
|||||||
|
|
||||||
unsigned id{0}; // implementation-level id
|
unsigned id{0}; // implementation-level id
|
||||||
int type{0}; // implementation type, not CS_PropertyKind!
|
int type{0}; // implementation type, not CS_PropertyKind!
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool DeviceGet(std::unique_lock<wpi::mutex>& lock,
|
||||||
|
IAMCameraControl* pProcAmp);
|
||||||
|
bool DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
||||||
|
IAMCameraControl* pProcAmp) const;
|
||||||
|
bool DeviceSet(std::unique_lock<wpi::mutex>& lock, IAMCameraControl* pProcAmp,
|
||||||
|
int newValue) const;
|
||||||
|
|
||||||
|
bool DeviceGet(std::unique_lock<wpi::mutex>& lock, IAMVideoProcAmp* pProcAmp);
|
||||||
|
bool DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
||||||
|
IAMVideoProcAmp* pProcAmp) const;
|
||||||
|
bool DeviceSet(std::unique_lock<wpi::mutex>& lock, IAMVideoProcAmp* pProcAmp,
|
||||||
|
int newValue) const;
|
||||||
};
|
};
|
||||||
} // namespace cs
|
} // namespace cs
|
||||||
|
|||||||
@@ -112,6 +112,19 @@ task generateJavaDocs(type: Javadoc) {
|
|||||||
source configurations.javaSource.collect { zipTree(it) }
|
source configurations.javaSource.collect { zipTree(it) }
|
||||||
include '**/*.java'
|
include '**/*.java'
|
||||||
failOnError = true
|
failOnError = true
|
||||||
|
|
||||||
|
title = "WPILib API $pubVersion"
|
||||||
|
ext.entryPoint = "$destinationDir/index.html"
|
||||||
|
|
||||||
|
if (JavaVersion.current().isJava11Compatible()) {
|
||||||
|
options.addBooleanOption('-no-module-directories', true)
|
||||||
|
doLast {
|
||||||
|
// This is a work-around for https://bugs.openjdk.java.net/browse/JDK-8211194. Can be removed once that issue is fixed on JDK's side
|
||||||
|
// Since JDK 11, package-list is missing from javadoc output files and superseded by element-list file, but a lot of external tools still need it
|
||||||
|
// Here we generate this file manually
|
||||||
|
new File(destinationDir, 'package-list').text = new File(destinationDir, 'element-list').text
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("zipJavaDocs", Zip) {
|
tasks.register("zipJavaDocs", Zip) {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ file(GLOB
|
|||||||
hal_shared_native_src src/main/native/cpp/cpp/*.cpp
|
hal_shared_native_src src/main/native/cpp/cpp/*.cpp
|
||||||
hal_shared_native_src src/main/native/cpp/handles/*.cpp
|
hal_shared_native_src src/main/native/cpp/handles/*.cpp
|
||||||
hal_sim_native_src src/main/native/sim/*.cpp
|
hal_sim_native_src src/main/native/sim/*.cpp
|
||||||
hal_sim_native_src src/main/native/sim/MockData/*.cpp)
|
hal_sim_native_src src/main/native/sim/mockdata/*.cpp)
|
||||||
add_library(hal ${hal_shared_native_src})
|
add_library(hal ${hal_shared_native_src})
|
||||||
set_target_properties(hal PROPERTIES DEBUG_POSTFIX "d")
|
set_target_properties(hal PROPERTIES DEBUG_POSTFIX "d")
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <wpi/mutex.h>
|
||||||
|
|
||||||
#include "HALInitializer.h"
|
#include "HALInitializer.h"
|
||||||
#include "PortsInternal.h"
|
#include "PortsInternal.h"
|
||||||
#include "hal/CANAPI.h"
|
#include "hal/CANAPI.h"
|
||||||
@@ -106,9 +108,16 @@ union PdpStatusEnergy {
|
|||||||
} bits;
|
} bits;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static wpi::mutex pdpHandleMutex;
|
||||||
|
static HAL_PDPHandle pdpHandles[kNumPDPModules];
|
||||||
|
|
||||||
namespace hal {
|
namespace hal {
|
||||||
namespace init {
|
namespace init {
|
||||||
void InitializePDP() {}
|
void InitializePDP() {
|
||||||
|
for (int i = 0; i < kNumPDPModules; i++) {
|
||||||
|
pdpHandles[i] = HAL_kInvalidHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace init
|
} // namespace init
|
||||||
} // namespace hal
|
} // namespace hal
|
||||||
|
|
||||||
@@ -121,6 +130,13 @@ HAL_PDPHandle HAL_InitializePDP(int32_t module, int32_t* status) {
|
|||||||
return HAL_kInvalidHandle;
|
return HAL_kInvalidHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::lock_guard<wpi::mutex> lock(pdpHandleMutex);
|
||||||
|
|
||||||
|
if (pdpHandles[module] != HAL_kInvalidHandle) {
|
||||||
|
*status = 0;
|
||||||
|
return pdpHandles[module];
|
||||||
|
}
|
||||||
|
|
||||||
auto handle = HAL_InitializeCAN(manufacturer, module, deviceType, status);
|
auto handle = HAL_InitializeCAN(manufacturer, module, deviceType, status);
|
||||||
|
|
||||||
if (*status != 0) {
|
if (*status != 0) {
|
||||||
@@ -128,10 +144,21 @@ HAL_PDPHandle HAL_InitializePDP(int32_t module, int32_t* status) {
|
|||||||
return HAL_kInvalidHandle;
|
return HAL_kInvalidHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pdpHandles[module] = handle;
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HAL_CleanPDP(HAL_PDPHandle handle) { HAL_CleanCAN(handle); }
|
void HAL_CleanPDP(HAL_PDPHandle handle) {
|
||||||
|
HAL_CleanCAN(handle);
|
||||||
|
|
||||||
|
for (int i = 0; i < kNumPDPModules; i++) {
|
||||||
|
if (pdpHandles[i] == handle) {
|
||||||
|
pdpHandles[i] = HAL_kInvalidHandle;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HAL_Bool HAL_CheckPDPModule(int32_t module) {
|
HAL_Bool HAL_CheckPDPModule(int32_t module) {
|
||||||
return module < kNumPDPModules && module >= 0;
|
return module < kNumPDPModules && module >= 0;
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ void ReportError(JNIEnv* env, int32_t status, bool doThrow) {
|
|||||||
ThrowUncleanStatusException(env, buf.c_str(), status);
|
ThrowUncleanStatusException(env, buf.c_str(), status);
|
||||||
} else {
|
} else {
|
||||||
std::string func;
|
std::string func;
|
||||||
auto stack = GetJavaStackTrace(env, &func, "edu.wpi.first.wpilibj");
|
auto stack = GetJavaStackTrace(env, &func, "edu.wpi.first");
|
||||||
HAL_SendError(1, status, 0, message, func.c_str(), stack.c_str(), 1);
|
HAL_SendError(1, status, 0, message, func.c_str(), stack.c_str(), 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,10 +109,6 @@ static wpi::StringRef UnescapeString(wpi::StringRef source,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
switch (*++s) {
|
switch (*++s) {
|
||||||
case '\\':
|
|
||||||
case '"':
|
|
||||||
buf.push_back(s[-1]);
|
|
||||||
break;
|
|
||||||
case 't':
|
case 't':
|
||||||
buf.push_back('\t');
|
buf.push_back('\t');
|
||||||
break;
|
break;
|
||||||
@@ -133,7 +129,7 @@ static wpi::StringRef UnescapeString(wpi::StringRef source,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
buf.push_back(s[-1]);
|
buf.push_back(*s);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ class StorageTestPersistent : public StorageTestEmpty {
|
|||||||
storage.SetEntryTypeValue("string/normal", Value::MakeString("hello"));
|
storage.SetEntryTypeValue("string/normal", Value::MakeString("hello"));
|
||||||
storage.SetEntryTypeValue("string/special",
|
storage.SetEntryTypeValue("string/special",
|
||||||
Value::MakeString(StringRef("\0\3\5\n", 4)));
|
Value::MakeString(StringRef("\0\3\5\n", 4)));
|
||||||
|
storage.SetEntryTypeValue("string/quoted", Value::MakeString("\"a\""));
|
||||||
storage.SetEntryTypeValue("raw/empty", Value::MakeRaw(""));
|
storage.SetEntryTypeValue("raw/empty", Value::MakeRaw(""));
|
||||||
storage.SetEntryTypeValue("raw/normal", Value::MakeRaw("hello"));
|
storage.SetEntryTypeValue("raw/normal", Value::MakeRaw("hello"));
|
||||||
storage.SetEntryTypeValue("raw/special",
|
storage.SetEntryTypeValue("raw/special",
|
||||||
@@ -599,6 +600,8 @@ TEST_P(StorageTestPersistent, SavePersistent) {
|
|||||||
std::tie(line, rem) = rem.split('\n');
|
std::tie(line, rem) = rem.split('\n');
|
||||||
ASSERT_EQ("string \"string/normal\"=\"hello\"", line);
|
ASSERT_EQ("string \"string/normal\"=\"hello\"", line);
|
||||||
std::tie(line, rem) = rem.split('\n');
|
std::tie(line, rem) = rem.split('\n');
|
||||||
|
ASSERT_EQ("string \"string/quoted\"=\"\\\"a\\\"\"", line);
|
||||||
|
std::tie(line, rem) = rem.split('\n');
|
||||||
ASSERT_EQ("string \"string/special\"=\"\\x00\\x03\\x05\\n\"", line);
|
ASSERT_EQ("string \"string/special\"=\"\\x00\\x03\\x05\\n\"", line);
|
||||||
std::tie(line, rem) = rem.split('\n');
|
std::tie(line, rem) = rem.split('\n');
|
||||||
ASSERT_EQ("array string \"stringarr/empty\"=", line);
|
ASSERT_EQ("array string \"stringarr/empty\"=", line);
|
||||||
@@ -787,18 +790,19 @@ TEST_P(StorageTestEmpty, LoadPersistent) {
|
|||||||
in += "string \"string/empty\"=\"\"\n";
|
in += "string \"string/empty\"=\"\"\n";
|
||||||
in += "string \"string/normal\"=\"hello\"\n";
|
in += "string \"string/normal\"=\"hello\"\n";
|
||||||
in += "string \"string/special\"=\"\\x00\\x03\\x05\\n\"\n";
|
in += "string \"string/special\"=\"\\x00\\x03\\x05\\n\"\n";
|
||||||
|
in += "string \"string/quoted\"=\"\\\"a\\\"\"\n";
|
||||||
in += "array string \"stringarr/empty\"=\n";
|
in += "array string \"stringarr/empty\"=\n";
|
||||||
in += "array string \"stringarr/one\"=\"hello\"\n";
|
in += "array string \"stringarr/one\"=\"hello\"\n";
|
||||||
in += "array string \"stringarr/two\"=\"hello\",\"world\\n\"\n";
|
in += "array string \"stringarr/two\"=\"hello\",\"world\\n\"\n";
|
||||||
|
|
||||||
EXPECT_CALL(dispatcher, QueueOutgoing(_, _, _)).Times(22);
|
EXPECT_CALL(dispatcher, QueueOutgoing(_, _, _)).Times(23);
|
||||||
EXPECT_CALL(notifier,
|
EXPECT_CALL(notifier,
|
||||||
NotifyEntry(_, _, _, NT_NOTIFY_NEW | NT_NOTIFY_LOCAL, UINT_MAX))
|
NotifyEntry(_, _, _, NT_NOTIFY_NEW | NT_NOTIFY_LOCAL, UINT_MAX))
|
||||||
.Times(22);
|
.Times(23);
|
||||||
|
|
||||||
wpi::raw_mem_istream iss(in);
|
wpi::raw_mem_istream iss(in);
|
||||||
EXPECT_TRUE(storage.LoadEntries(iss, "", true, warn_func));
|
EXPECT_TRUE(storage.LoadEntries(iss, "", true, warn_func));
|
||||||
ASSERT_EQ(22u, entries().size());
|
ASSERT_EQ(23u, entries().size());
|
||||||
|
|
||||||
EXPECT_EQ(*Value::MakeBoolean(true), *storage.GetEntryValue("boolean/true"));
|
EXPECT_EQ(*Value::MakeBoolean(true), *storage.GetEntryValue("boolean/true"));
|
||||||
EXPECT_EQ(*Value::MakeBoolean(false),
|
EXPECT_EQ(*Value::MakeBoolean(false),
|
||||||
@@ -811,6 +815,8 @@ TEST_P(StorageTestEmpty, LoadPersistent) {
|
|||||||
*storage.GetEntryValue("string/normal"));
|
*storage.GetEntryValue("string/normal"));
|
||||||
EXPECT_EQ(*Value::MakeString(StringRef("\0\3\5\n", 4)),
|
EXPECT_EQ(*Value::MakeString(StringRef("\0\3\5\n", 4)),
|
||||||
*storage.GetEntryValue("string/special"));
|
*storage.GetEntryValue("string/special"));
|
||||||
|
EXPECT_EQ(*Value::MakeString("\"a\""),
|
||||||
|
*storage.GetEntryValue("string/quoted"));
|
||||||
EXPECT_EQ(*Value::MakeRaw(""), *storage.GetEntryValue("raw/empty"));
|
EXPECT_EQ(*Value::MakeRaw(""), *storage.GetEntryValue("raw/empty"));
|
||||||
EXPECT_EQ(*Value::MakeRaw("hello"), *storage.GetEntryValue("raw/normal"));
|
EXPECT_EQ(*Value::MakeRaw("hello"), *storage.GetEntryValue("raw/normal"));
|
||||||
EXPECT_EQ(*Value::MakeRaw(StringRef("\0\3\5\n", 4)),
|
EXPECT_EQ(*Value::MakeRaw(StringRef("\0\3\5\n", 4)),
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ DSCommPacket::DSCommPacket() {
|
|||||||
i.ResetTcp();
|
i.ResetTcp();
|
||||||
i.ResetUdp();
|
i.ResetUdp();
|
||||||
}
|
}
|
||||||
|
matchInfo.gameSpecificMessageSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------
|
/*----------------------------------------------------------------------------
|
||||||
@@ -95,7 +96,7 @@ void DSCommPacket::ReadJoystickTag(wpi::ArrayRef<uint8_t> dataInput,
|
|||||||
int numBytes = (buttonCount + 7) / 8;
|
int numBytes = (buttonCount + 7) / 8;
|
||||||
stick.buttons.buttons = 0;
|
stick.buttons.buttons = 0;
|
||||||
for (int i = 0; i < numBytes; i++) {
|
for (int i = 0; i < numBytes; i++) {
|
||||||
stick.buttons.buttons |= dataInput[1 + i] << (8 * (i));
|
stick.buttons.buttons |= dataInput[numBytes - i] << (8 * (i));
|
||||||
}
|
}
|
||||||
stick.buttons.count = buttonCount;
|
stick.buttons.count = buttonCount;
|
||||||
|
|
||||||
|
|||||||
@@ -66,13 +66,21 @@ TEST_F(DSCommPacketTest, BlankJoystickTag) {
|
|||||||
|
|
||||||
TEST_F(DSCommPacketTest, MainJoystickTag) {
|
TEST_F(DSCommPacketTest, MainJoystickTag) {
|
||||||
for (int i = 0; i < HAL_kMaxJoysticks; i++) {
|
for (int i = 0; i < HAL_kMaxJoysticks; i++) {
|
||||||
|
// Just random data
|
||||||
|
std::array<uint8_t, 12> _buttons{{0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1}};
|
||||||
|
|
||||||
|
std::array<uint8_t, 2> _button_bytes{{0, 0}};
|
||||||
|
for (int btn = 0; btn < 8; btn++) _button_bytes[1] |= _buttons[btn] << btn;
|
||||||
|
for (int btn = 8; btn < 12; btn++)
|
||||||
|
_button_bytes[0] |= _buttons[btn] << (btn - 8);
|
||||||
|
|
||||||
// 5 for base, 4 joystick, 12 buttons (2 bytes) 3 povs
|
// 5 for base, 4 joystick, 12 buttons (2 bytes) 3 povs
|
||||||
uint8_t arr[5 + 4 + 2 + 6] = {// Size, Tag
|
uint8_t arr[5 + 4 + 2 + 6] = {// Size, Tag
|
||||||
16, 12,
|
16, 12,
|
||||||
// Axes
|
// Axes
|
||||||
4, 0x9C, 0xCE, 0, 75,
|
4, 0x9C, 0xCE, 0, 75,
|
||||||
// Buttons
|
// Buttons (LSB 0)
|
||||||
12, 0xFF, 0x0F,
|
12, _button_bytes[0], _button_bytes[1],
|
||||||
// POVs
|
// POVs
|
||||||
3, 0, 50, 0, 100, 0x0F, 0x00};
|
3, 0, 50, 0, 100, 0x0F, 0x00};
|
||||||
|
|
||||||
@@ -80,6 +88,11 @@ TEST_F(DSCommPacketTest, MainJoystickTag) {
|
|||||||
ASSERT_EQ(data.axes.count, 4);
|
ASSERT_EQ(data.axes.count, 4);
|
||||||
ASSERT_EQ(data.povs.count, 3);
|
ASSERT_EQ(data.povs.count, 3);
|
||||||
ASSERT_EQ(data.buttons.count, 12);
|
ASSERT_EQ(data.buttons.count, 12);
|
||||||
|
|
||||||
|
for (int btn = 0; btn < 12; btn++) {
|
||||||
|
ASSERT_EQ((data.buttons.buttons & (1 << btn)) != 0, _buttons[btn] != 0)
|
||||||
|
<< "Button " << btn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,29 +26,29 @@ IterativeRobotBase::IterativeRobotBase(double period)
|
|||||||
m_watchdog(period, [this] { PrintLoopOverrunMessage(); }) {}
|
m_watchdog(period, [this] { PrintLoopOverrunMessage(); }) {}
|
||||||
|
|
||||||
void IterativeRobotBase::RobotInit() {
|
void IterativeRobotBase::RobotInit() {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void IterativeRobotBase::DisabledInit() {
|
void IterativeRobotBase::DisabledInit() {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void IterativeRobotBase::AutonomousInit() {
|
void IterativeRobotBase::AutonomousInit() {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void IterativeRobotBase::TeleopInit() {
|
void IterativeRobotBase::TeleopInit() {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void IterativeRobotBase::TestInit() {
|
void IterativeRobotBase::TestInit() {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void IterativeRobotBase::RobotPeriodic() {
|
void IterativeRobotBase::RobotPeriodic() {
|
||||||
static bool firstRun = true;
|
static bool firstRun = true;
|
||||||
if (firstRun) {
|
if (firstRun) {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
firstRun = false;
|
firstRun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ void IterativeRobotBase::RobotPeriodic() {
|
|||||||
void IterativeRobotBase::DisabledPeriodic() {
|
void IterativeRobotBase::DisabledPeriodic() {
|
||||||
static bool firstRun = true;
|
static bool firstRun = true;
|
||||||
if (firstRun) {
|
if (firstRun) {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
firstRun = false;
|
firstRun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ void IterativeRobotBase::DisabledPeriodic() {
|
|||||||
void IterativeRobotBase::AutonomousPeriodic() {
|
void IterativeRobotBase::AutonomousPeriodic() {
|
||||||
static bool firstRun = true;
|
static bool firstRun = true;
|
||||||
if (firstRun) {
|
if (firstRun) {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
firstRun = false;
|
firstRun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ void IterativeRobotBase::AutonomousPeriodic() {
|
|||||||
void IterativeRobotBase::TeleopPeriodic() {
|
void IterativeRobotBase::TeleopPeriodic() {
|
||||||
static bool firstRun = true;
|
static bool firstRun = true;
|
||||||
if (firstRun) {
|
if (firstRun) {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
firstRun = false;
|
firstRun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@ void IterativeRobotBase::TeleopPeriodic() {
|
|||||||
void IterativeRobotBase::TestPeriodic() {
|
void IterativeRobotBase::TestPeriodic() {
|
||||||
static bool firstRun = true;
|
static bool firstRun = true;
|
||||||
if (firstRun) {
|
if (firstRun) {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
firstRun = false;
|
firstRun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,23 +59,23 @@ void SampleRobot::StartCompetition() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SampleRobot::RobotInit() {
|
void SampleRobot::RobotInit() {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampleRobot::Disabled() {
|
void SampleRobot::Disabled() {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampleRobot::Autonomous() {
|
void SampleRobot::Autonomous() {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampleRobot::OperatorControl() {
|
void SampleRobot::OperatorControl() {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampleRobot::Test() {
|
void SampleRobot::Test() {
|
||||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampleRobot::RobotMain() { m_robotMainOverridden = false; }
|
void SampleRobot::RobotMain() { m_robotMainOverridden = false; }
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ constexpr std::chrono::milliseconds Watchdog::kMinPrintPeriod;
|
|||||||
class Watchdog::Thread : public wpi::SafeThread {
|
class Watchdog::Thread : public wpi::SafeThread {
|
||||||
public:
|
public:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct DerefGreater : public std::binary_function<T, T, bool> {
|
struct DerefGreater {
|
||||||
constexpr bool operator()(const T& lhs, const T& rhs) const {
|
constexpr bool operator()(const T& lhs, const T& rhs) const {
|
||||||
return *lhs > *rhs;
|
return *lhs > *rhs;
|
||||||
}
|
}
|
||||||
@@ -160,8 +160,6 @@ void Watchdog::Disable() {
|
|||||||
auto thr = m_owner->GetThread();
|
auto thr = m_owner->GetThread();
|
||||||
if (!thr) return;
|
if (!thr) return;
|
||||||
|
|
||||||
m_isExpired = false;
|
|
||||||
|
|
||||||
thr->m_watchdogs.remove(this);
|
thr->m_watchdogs.remove(this);
|
||||||
thr->m_cond.notify_all();
|
thr->m_cond.notify_all();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "frc/shuffleboard/BuiltInLayouts.h"
|
|
||||||
|
|
||||||
using namespace frc;
|
|
||||||
|
|
||||||
const LayoutType BuiltInLayouts::kList{"List Layout"};
|
|
||||||
const LayoutType BuiltInLayouts::kGrid{"Grid Layout"};
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "frc/shuffleboard/BuiltInWidgets.h"
|
|
||||||
|
|
||||||
using namespace frc;
|
|
||||||
|
|
||||||
const WidgetType BuiltInWidgets::kTextView{"Text View"};
|
|
||||||
const WidgetType BuiltInWidgets::kNumberSlider{"Number Slider"};
|
|
||||||
const WidgetType BuiltInWidgets::kNumberBar{"Number Bar"};
|
|
||||||
const WidgetType BuiltInWidgets::kDial{"Simple Dial"};
|
|
||||||
const WidgetType BuiltInWidgets::kGraph{"Graph"};
|
|
||||||
const WidgetType BuiltInWidgets::kBooleanBox{"Boolean Box"};
|
|
||||||
const WidgetType BuiltInWidgets::kToggleButton{"Toggle Button"};
|
|
||||||
const WidgetType BuiltInWidgets::kToggleSwitch{"Toggle Switch"};
|
|
||||||
const WidgetType BuiltInWidgets::kVoltageView{"Voltage View"};
|
|
||||||
const WidgetType BuiltInWidgets::kPowerDistributionPanel{"PDP"};
|
|
||||||
const WidgetType BuiltInWidgets::kComboBoxChooser{"ComboBox Chooser"};
|
|
||||||
const WidgetType BuiltInWidgets::kSplitButtonChooser{"Split Button Chooser"};
|
|
||||||
const WidgetType BuiltInWidgets::kEncoder{"Encoder"};
|
|
||||||
const WidgetType BuiltInWidgets::kSpeedController{"Speed Controller"};
|
|
||||||
const WidgetType BuiltInWidgets::kCommand{"Command"};
|
|
||||||
const WidgetType BuiltInWidgets::kPIDCommand{"PID Command"};
|
|
||||||
const WidgetType BuiltInWidgets::kPIDController{"PID Controller"};
|
|
||||||
const WidgetType BuiltInWidgets::kAccelerometer{"Accelerometer"};
|
|
||||||
const WidgetType BuiltInWidgets::k3AxisAccelerometer{"3-Axis Accelerometer"};
|
|
||||||
const WidgetType BuiltInWidgets::kGyro{"Gyro"};
|
|
||||||
const WidgetType BuiltInWidgets::kRelay{"Relay"};
|
|
||||||
const WidgetType BuiltInWidgets::kDifferentialDrive{"Differential Drivebase"};
|
|
||||||
const WidgetType BuiltInWidgets::kMecanumDrive{"Mecanum Drivebase"};
|
|
||||||
const WidgetType BuiltInWidgets::kCameraStream{"Camera Stream"};
|
|
||||||
@@ -18,6 +18,12 @@
|
|||||||
|
|
||||||
using namespace frc;
|
using namespace frc;
|
||||||
|
|
||||||
|
static constexpr const char* layoutStrings[] = {"List Layout", "Grid Layout"};
|
||||||
|
|
||||||
|
static constexpr const char* GetStringFromBuiltInLayout(BuiltInLayouts layout) {
|
||||||
|
return layoutStrings[static_cast<int>(layout)];
|
||||||
|
}
|
||||||
|
|
||||||
ShuffleboardContainer::ShuffleboardContainer(const wpi::Twine& title)
|
ShuffleboardContainer::ShuffleboardContainer(const wpi::Twine& title)
|
||||||
: ShuffleboardValue(title) {}
|
: ShuffleboardValue(title) {}
|
||||||
|
|
||||||
@@ -26,6 +32,11 @@ ShuffleboardContainer::GetComponents() const {
|
|||||||
return m_components;
|
return m_components;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& title,
|
||||||
|
BuiltInLayouts type) {
|
||||||
|
return GetLayout(title, GetStringFromBuiltInLayout(type));
|
||||||
|
}
|
||||||
|
|
||||||
ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& title,
|
ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& title,
|
||||||
const LayoutType& type) {
|
const LayoutType& type) {
|
||||||
return GetLayout(title, type.GetLayoutName());
|
return GetLayout(title, type.GetLayoutName());
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
||||||
|
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||||
|
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||||
|
/* the project. */
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "frc/shuffleboard/ShuffleboardWidget.h"
|
||||||
|
|
||||||
|
using namespace frc;
|
||||||
|
|
||||||
|
static constexpr const char* widgetStrings[] = {
|
||||||
|
"Text View",
|
||||||
|
"Number Slider",
|
||||||
|
"Number Bar",
|
||||||
|
"Simple Dial",
|
||||||
|
"Graph",
|
||||||
|
"Boolean Box",
|
||||||
|
"Toggle Button",
|
||||||
|
"Toggle Switch",
|
||||||
|
"Voltage View",
|
||||||
|
"PDP",
|
||||||
|
"ComboBox Chooser",
|
||||||
|
"Split Button Chooser",
|
||||||
|
"Encoder",
|
||||||
|
"Speed Controller",
|
||||||
|
"Command",
|
||||||
|
"PID Command",
|
||||||
|
"PID Controller",
|
||||||
|
"Accelerometer",
|
||||||
|
"3-Axis Accelerometer",
|
||||||
|
"Gyro",
|
||||||
|
"Relay",
|
||||||
|
"Differential Drivebase",
|
||||||
|
"Mecanum Drivebase",
|
||||||
|
"Camera Stream",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* detail::GetStringForWidgetType(BuiltInWidgets type) {
|
||||||
|
return widgetStrings[static_cast<int>(type)];
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "frc/RobotBase.h"
|
#include "frc/RobotBase.h"
|
||||||
|
|
||||||
namespace frc {
|
namespace frc {
|
||||||
|
/** WPILib FileSystem namespace */
|
||||||
namespace filesystem {
|
namespace filesystem {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ namespace frc {
|
|||||||
* .GetLayout(BuiltinLayouts::kList, "My List");
|
* .GetLayout(BuiltinLayouts::kList, "My List");
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*/
|
*/
|
||||||
class BuiltInLayouts {
|
enum class BuiltInLayouts {
|
||||||
public:
|
|
||||||
/**
|
/**
|
||||||
* Groups components in a vertical list. New widgets added to the layout will
|
* Groups components in a vertical list. New widgets added to the layout will
|
||||||
* be placed at the bottom of the list. <br>Custom properties: <table>
|
* be placed at the bottom of the list. <br>Custom properties: <table>
|
||||||
@@ -30,7 +29,7 @@ class BuiltInLayouts {
|
|||||||
* {@code ["TOP", "LEFT", "BOTTOM", "RIGHT", "HIDDEN"}</td></tr>
|
* {@code ["TOP", "LEFT", "BOTTOM", "RIGHT", "HIDDEN"}</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const LayoutType kList;
|
kList,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Groups components in an <i>n</i> x <i>m</i> grid. Grid layouts default to
|
* Groups components in an <i>n</i> x <i>m</i> grid. Grid layouts default to
|
||||||
@@ -47,7 +46,7 @@ class BuiltInLayouts {
|
|||||||
* </tr>
|
* </tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const LayoutType kGrid;
|
kGrid
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace frc
|
} // namespace frc
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ namespace frc {
|
|||||||
* <p>Each value in this enum goes into detail on what data types that widget
|
* <p>Each value in this enum goes into detail on what data types that widget
|
||||||
* can support, as well as the custom properties that widget uses.
|
* can support, as well as the custom properties that widget uses.
|
||||||
*/
|
*/
|
||||||
class BuiltInWidgets {
|
enum class BuiltInWidgets {
|
||||||
public:
|
|
||||||
/**
|
/**
|
||||||
* Displays a value with a simple text field.
|
* Displays a value with a simple text field.
|
||||||
* <br>Supported types:
|
* <br>Supported types:
|
||||||
@@ -37,7 +36,7 @@ class BuiltInWidgets {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* <br>This widget has no custom properties.
|
* <br>This widget has no custom properties.
|
||||||
*/
|
*/
|
||||||
static const WidgetType kTextView;
|
kTextView,
|
||||||
/**
|
/**
|
||||||
* Displays a number with a controllable slider.
|
* Displays a number with a controllable slider.
|
||||||
* <br>Supported types:
|
* <br>Supported types:
|
||||||
@@ -54,7 +53,7 @@ class BuiltInWidgets {
|
|||||||
* slider by with the arrow keys</td></tr>
|
* slider by with the arrow keys</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType kNumberSlider;
|
kNumberSlider,
|
||||||
/**
|
/**
|
||||||
* Displays a number with a view-only bar.
|
* Displays a number with a view-only bar.
|
||||||
* <br>Supported types:
|
* <br>Supported types:
|
||||||
@@ -71,7 +70,7 @@ class BuiltInWidgets {
|
|||||||
* of the bar</td></tr>
|
* of the bar</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType kNumberBar;
|
kNumberBar,
|
||||||
/**
|
/**
|
||||||
* Displays a number with a view-only dial. Displayed values are rounded to
|
* Displays a number with a view-only dial. Displayed values are rounded to
|
||||||
* the nearest integer. <br>Supported types: <ul> <li>Number</li>
|
* the nearest integer. <br>Supported types: <ul> <li>Number</li>
|
||||||
@@ -86,7 +85,7 @@ class BuiltInWidgets {
|
|||||||
* value as text</td></tr>
|
* value as text</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType kDial;
|
kDial,
|
||||||
/**
|
/**
|
||||||
* Displays a number with a graph. <strong>NOTE:</strong> graphs can be taxing
|
* Displays a number with a graph. <strong>NOTE:</strong> graphs can be taxing
|
||||||
* on the computer running the dashboard. Keep the number of visible data
|
* on the computer running the dashboard. Keep the number of visible data
|
||||||
@@ -103,7 +102,7 @@ class BuiltInWidgets {
|
|||||||
* <td>How long, in seconds, should past data be visible for</td></tr>
|
* <td>How long, in seconds, should past data be visible for</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType kGraph;
|
kGraph,
|
||||||
/**
|
/**
|
||||||
* Displays a boolean value as a large colored box.
|
* Displays a boolean value as a large colored box.
|
||||||
* <br>Supported types:
|
* <br>Supported types:
|
||||||
@@ -121,7 +120,7 @@ class BuiltInWidgets {
|
|||||||
* <td>Can be specified as a string or a number</td></tr>
|
* <td>Can be specified as a string or a number</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType kBooleanBox;
|
kBooleanBox,
|
||||||
/**
|
/**
|
||||||
* Displays a boolean with a large interactive toggle button.
|
* Displays a boolean with a large interactive toggle button.
|
||||||
* <br>Supported types:
|
* <br>Supported types:
|
||||||
@@ -130,7 +129,7 @@ class BuiltInWidgets {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* <br>This widget has no custom properties.
|
* <br>This widget has no custom properties.
|
||||||
*/
|
*/
|
||||||
static const WidgetType kToggleButton;
|
kToggleButton,
|
||||||
/**
|
/**
|
||||||
* Displays a boolean with a fixed-size toggle switch.
|
* Displays a boolean with a fixed-size toggle switch.
|
||||||
* <br>Supported types:
|
* <br>Supported types:
|
||||||
@@ -139,7 +138,7 @@ class BuiltInWidgets {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* <br>This widget has no custom properties.
|
* <br>This widget has no custom properties.
|
||||||
*/
|
*/
|
||||||
static const WidgetType kToggleSwitch;
|
kToggleSwitch,
|
||||||
/**
|
/**
|
||||||
* Displays an analog input or a raw number with a number bar.
|
* Displays an analog input or a raw number with a number bar.
|
||||||
* <br>Supported types:
|
* <br>Supported types:
|
||||||
@@ -162,7 +161,7 @@ class BuiltInWidgets {
|
|||||||
* bar</td></tr>
|
* bar</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType kVoltageView;
|
kVoltageView,
|
||||||
/**
|
/**
|
||||||
* Displays a {@link edu.wpi.first.wpilibj.PowerDistributionPanel
|
* Displays a {@link edu.wpi.first.wpilibj.PowerDistributionPanel
|
||||||
* PowerDistributionPanel}. <br>Supported types: <ul> <li>{@link
|
* PowerDistributionPanel}. <br>Supported types: <ul> <li>{@link
|
||||||
@@ -175,7 +174,7 @@ class BuiltInWidgets {
|
|||||||
* <td>Whether or not to display the voltage and current draw</td></tr>
|
* <td>Whether or not to display the voltage and current draw</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType kPowerDistributionPanel;
|
kPowerDistributionPanel,
|
||||||
/**
|
/**
|
||||||
* Displays a {@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser
|
* Displays a {@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser
|
||||||
* SendableChooser} with a dropdown combo box with a list of options.
|
* SendableChooser} with a dropdown combo box with a list of options.
|
||||||
@@ -185,7 +184,7 @@ class BuiltInWidgets {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* <br>This widget has no custom properties.
|
* <br>This widget has no custom properties.
|
||||||
*/
|
*/
|
||||||
static const WidgetType kComboBoxChooser;
|
kComboBoxChooser,
|
||||||
/**
|
/**
|
||||||
* Displays a {@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser
|
* Displays a {@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser
|
||||||
* SendableChooser} with a toggle button for each available option.
|
* SendableChooser} with a toggle button for each available option.
|
||||||
@@ -195,7 +194,7 @@ class BuiltInWidgets {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* <br>This widget has no custom properties.
|
* <br>This widget has no custom properties.
|
||||||
*/
|
*/
|
||||||
static const WidgetType kSplitButtonChooser;
|
kSplitButtonChooser,
|
||||||
/**
|
/**
|
||||||
* Displays an {@link edu.wpi.first.wpilibj.Encoder} displaying its speed,
|
* Displays an {@link edu.wpi.first.wpilibj.Encoder} displaying its speed,
|
||||||
* total travelled distance, and its distance per tick. <br>Supported types:
|
* total travelled distance, and its distance per tick. <br>Supported types:
|
||||||
@@ -204,7 +203,7 @@ class BuiltInWidgets {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* <br>This widget has no custom properties.
|
* <br>This widget has no custom properties.
|
||||||
*/
|
*/
|
||||||
static const WidgetType kEncoder;
|
kEncoder,
|
||||||
/**
|
/**
|
||||||
* Displays a {@link edu.wpi.first.wpilibj.SpeedController SpeedController}.
|
* Displays a {@link edu.wpi.first.wpilibj.SpeedController SpeedController}.
|
||||||
* The speed controller will be controllable from the dashboard when test mode
|
* The speed controller will be controllable from the dashboard when test mode
|
||||||
@@ -228,7 +227,7 @@ class BuiltInWidgets {
|
|||||||
* <td>One of {@code ["HORIZONTAL", "VERTICAL"]}</td></tr>
|
* <td>One of {@code ["HORIZONTAL", "VERTICAL"]}</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType kSpeedController;
|
kSpeedController,
|
||||||
/**
|
/**
|
||||||
* Displays a command with a toggle button. Pressing the button will start the
|
* Displays a command with a toggle button. Pressing the button will start the
|
||||||
* command, and the button will automatically release when the command
|
* command, and the button will automatically release when the command
|
||||||
@@ -239,7 +238,7 @@ class BuiltInWidgets {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* <br>This widget has no custom properties.
|
* <br>This widget has no custom properties.
|
||||||
*/
|
*/
|
||||||
static const WidgetType kCommand;
|
kCommand,
|
||||||
/**
|
/**
|
||||||
* Displays a PID command with a checkbox and an editor for the PIDF
|
* Displays a PID command with a checkbox and an editor for the PIDF
|
||||||
* constants. Selecting the checkbox will start the command, and the checkbox
|
* constants. Selecting the checkbox will start the command, and the checkbox
|
||||||
@@ -249,7 +248,7 @@ class BuiltInWidgets {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* <br>This widget has no custom properties.
|
* <br>This widget has no custom properties.
|
||||||
*/
|
*/
|
||||||
static const WidgetType kPIDCommand;
|
kPIDCommand,
|
||||||
/**
|
/**
|
||||||
* Displays a PID controller with an editor for the PIDF constants and a
|
* Displays a PID controller with an editor for the PIDF constants and a
|
||||||
* toggle switch for enabling and disabling the controller. <br>Supported
|
* toggle switch for enabling and disabling the controller. <br>Supported
|
||||||
@@ -257,7 +256,7 @@ class BuiltInWidgets {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* <br>This widget has no custom properties.
|
* <br>This widget has no custom properties.
|
||||||
*/
|
*/
|
||||||
static const WidgetType kPIDController;
|
kPIDController,
|
||||||
/**
|
/**
|
||||||
* Displays an accelerometer with a number bar displaying the magnitude of the
|
* Displays an accelerometer with a number bar displaying the magnitude of the
|
||||||
* acceleration and text displaying the exact value. <br>Supported types: <ul>
|
* acceleration and text displaying the exact value. <br>Supported types: <ul>
|
||||||
@@ -278,7 +277,7 @@ class BuiltInWidgets {
|
|||||||
* <td>Show or hide the tick marks on the number bars</td></tr>
|
* <td>Show or hide the tick marks on the number bars</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType kAccelerometer;
|
kAccelerometer,
|
||||||
/**
|
/**
|
||||||
* Displays a 3-axis accelerometer with a number bar for each axis'
|
* Displays a 3-axis accelerometer with a number bar for each axis'
|
||||||
* accleration. <br>Supported types: <ul> <li>{@link
|
* accleration. <br>Supported types: <ul> <li>{@link
|
||||||
@@ -298,7 +297,7 @@ class BuiltInWidgets {
|
|||||||
* <td>Show or hide the tick marks on the number bars</td></tr>
|
* <td>Show or hide the tick marks on the number bars</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType k3AxisAccelerometer;
|
k3AxisAccelerometer,
|
||||||
/**
|
/**
|
||||||
* Displays a gyro with a dial from 0 to 360 degrees.
|
* Displays a gyro with a dial from 0 to 360 degrees.
|
||||||
* <br>Supported types:
|
* <br>Supported types:
|
||||||
@@ -317,7 +316,7 @@ class BuiltInWidgets {
|
|||||||
* <tr><td>Show tick mark ring</td><td>Boolean</td><td>true</td></tr>
|
* <tr><td>Show tick mark ring</td><td>Boolean</td><td>true</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType kGyro;
|
kGyro,
|
||||||
/**
|
/**
|
||||||
* Displays a relay with toggle buttons for each supported mode (off, on,
|
* Displays a relay with toggle buttons for each supported mode (off, on,
|
||||||
* forward, reverse). <br>Supported types: <ul> <li>{@link
|
* forward, reverse). <br>Supported types: <ul> <li>{@link
|
||||||
@@ -325,7 +324,7 @@ class BuiltInWidgets {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* <br>This widget has no custom properties.
|
* <br>This widget has no custom properties.
|
||||||
*/
|
*/
|
||||||
static const WidgetType kRelay;
|
kRelay,
|
||||||
/**
|
/**
|
||||||
* Displays a differential drive with a widget that displays the speed of each
|
* Displays a differential drive with a widget that displays the speed of each
|
||||||
* side of the drivebase and a vector for the direction and rotation of the
|
* side of the drivebase and a vector for the direction and rotation of the
|
||||||
@@ -344,7 +343,7 @@ class BuiltInWidgets {
|
|||||||
* <tr><td>Show velocity vectors</td><td>Boolean</td><td>true</td></tr>
|
* <tr><td>Show velocity vectors</td><td>Boolean</td><td>true</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType kDifferentialDrive;
|
kDifferentialDrive,
|
||||||
/**
|
/**
|
||||||
* Displays a mecanum drive with a widget that displays the speed of each
|
* Displays a mecanum drive with a widget that displays the speed of each
|
||||||
* wheel, and vectors for the direction and rotation of the drivebase. The
|
* wheel, and vectors for the direction and rotation of the drivebase. The
|
||||||
@@ -357,7 +356,7 @@ class BuiltInWidgets {
|
|||||||
* <tr><td>Show velocity vectors</td><td>Boolean</td><td>true</td></tr>
|
* <tr><td>Show velocity vectors</td><td>Boolean</td><td>true</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType kMecanumDrive;
|
kMecanumDrive,
|
||||||
/**
|
/**
|
||||||
* Displays a camera stream.
|
* Displays a camera stream.
|
||||||
* <br>Supported types:
|
* <br>Supported types:
|
||||||
@@ -381,7 +380,7 @@ class BuiltInWidgets {
|
|||||||
* </td></tr>
|
* </td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
static const WidgetType kCameraStream;
|
kCameraStream
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace frc
|
} // namespace frc
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <wpi/Twine.h>
|
#include <wpi/StringRef.h>
|
||||||
|
|
||||||
namespace frc {
|
namespace frc {
|
||||||
|
|
||||||
@@ -20,7 +20,8 @@ namespace frc {
|
|||||||
*/
|
*/
|
||||||
class LayoutType {
|
class LayoutType {
|
||||||
public:
|
public:
|
||||||
explicit LayoutType(const char* layoutName) : m_layoutName(layoutName) {}
|
explicit constexpr LayoutType(const char* layoutName)
|
||||||
|
: m_layoutName(layoutName) {}
|
||||||
~LayoutType() = default;
|
~LayoutType() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +31,7 @@ class LayoutType {
|
|||||||
wpi::StringRef GetLayoutName() const;
|
wpi::StringRef GetLayoutName() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wpi::StringRef m_layoutName;
|
const char* m_layoutName;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace frc
|
} // namespace frc
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "frc/ErrorBase.h"
|
#include "frc/ErrorBase.h"
|
||||||
#include "frc/WPIErrors.h"
|
#include "frc/WPIErrors.h"
|
||||||
|
#include "frc/shuffleboard/BuiltInLayouts.h"
|
||||||
#include "frc/shuffleboard/LayoutType.h"
|
#include "frc/shuffleboard/LayoutType.h"
|
||||||
#include "frc/shuffleboard/ShuffleboardComponentBase.h"
|
#include "frc/shuffleboard/ShuffleboardComponentBase.h"
|
||||||
#include "frc/shuffleboard/ShuffleboardValue.h"
|
#include "frc/shuffleboard/ShuffleboardValue.h"
|
||||||
@@ -53,6 +54,16 @@ class ShuffleboardContainer : public virtual ShuffleboardValue,
|
|||||||
const std::vector<std::unique_ptr<ShuffleboardComponentBase>>& GetComponents()
|
const std::vector<std::unique_ptr<ShuffleboardComponentBase>>& GetComponents()
|
||||||
const;
|
const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the layout with the given type and title, creating it if it does not
|
||||||
|
* already exist at the time this method is called.
|
||||||
|
*
|
||||||
|
* @param title the title of the layout
|
||||||
|
* @param layoutType the type of the layout, eg "List" or "Grid"
|
||||||
|
* @return the layout
|
||||||
|
*/
|
||||||
|
ShuffleboardLayout& GetLayout(const wpi::Twine& title, BuiltInLayouts type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the layout with the given type and title, creating it if it does not
|
* Gets the layout with the given type and title, creating it if it does not
|
||||||
* already exist at the time this method is called.
|
* already exist at the time this method is called.
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <wpi/Twine.h>
|
#include <wpi/Twine.h>
|
||||||
|
|
||||||
|
#include "frc/shuffleboard/BuiltInWidgets.h"
|
||||||
#include "frc/shuffleboard/ShuffleboardComponent.h"
|
#include "frc/shuffleboard/ShuffleboardComponent.h"
|
||||||
#include "frc/shuffleboard/WidgetType.h"
|
#include "frc/shuffleboard/WidgetType.h"
|
||||||
|
|
||||||
@@ -16,6 +17,10 @@ namespace frc {
|
|||||||
|
|
||||||
class ShuffleboardContainer;
|
class ShuffleboardContainer;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
const char* GetStringForWidgetType(BuiltInWidgets type);
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract superclass for widgets.
|
* Abstract superclass for widgets.
|
||||||
*
|
*
|
||||||
@@ -24,12 +29,11 @@ class ShuffleboardContainer;
|
|||||||
* @tparam Derived the self type
|
* @tparam Derived the self type
|
||||||
*/
|
*/
|
||||||
template <typename Derived>
|
template <typename Derived>
|
||||||
class ShuffleboardWidget
|
class ShuffleboardWidget : public ShuffleboardComponent<Derived> {
|
||||||
: public ShuffleboardComponent<ShuffleboardWidget<Derived>> {
|
|
||||||
public:
|
public:
|
||||||
ShuffleboardWidget(ShuffleboardContainer& parent, const wpi::Twine& title)
|
ShuffleboardWidget(ShuffleboardContainer& parent, const wpi::Twine& title)
|
||||||
: ShuffleboardValue(title),
|
: ShuffleboardValue(title),
|
||||||
ShuffleboardComponent<ShuffleboardWidget<Derived>>(parent, title) {}
|
ShuffleboardComponent<Derived>(parent, title) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the type of widget used to display the data. If not set, the default
|
* Sets the type of widget used to display the data. If not set, the default
|
||||||
@@ -39,7 +43,18 @@ class ShuffleboardWidget
|
|||||||
* @return this widget object
|
* @return this widget object
|
||||||
* @see BuiltInWidgets
|
* @see BuiltInWidgets
|
||||||
*/
|
*/
|
||||||
Derived& withWidget(const WidgetType& widgetType) {
|
Derived& WithWidget(BuiltInWidgets widgetType) {
|
||||||
|
return WithWidget(detail::GetStringForWidgetType(widgetType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the type of widget used to display the data. If not set, the default
|
||||||
|
* widget type will be used.
|
||||||
|
*
|
||||||
|
* @param widgetType the type of the widget used to display the data
|
||||||
|
* @return this widget object
|
||||||
|
*/
|
||||||
|
Derived& WithWidget(const WidgetType& widgetType) {
|
||||||
return WithWidget(widgetType.GetWidgetName());
|
return WithWidget(widgetType.GetWidgetName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <wpi/Twine.h>
|
#include <wpi/StringRef.h>
|
||||||
|
|
||||||
namespace frc {
|
namespace frc {
|
||||||
|
|
||||||
@@ -20,7 +20,8 @@ namespace frc {
|
|||||||
*/
|
*/
|
||||||
class WidgetType {
|
class WidgetType {
|
||||||
public:
|
public:
|
||||||
explicit WidgetType(const char* widgetName) : m_widgetName(widgetName) {}
|
explicit constexpr WidgetType(const char* widgetName)
|
||||||
|
: m_widgetName(widgetName) {}
|
||||||
~WidgetType() = default;
|
~WidgetType() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +31,7 @@ class WidgetType {
|
|||||||
wpi::StringRef GetWidgetName() const;
|
wpi::StringRef GetWidgetName() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wpi::StringRef m_widgetName;
|
const char* m_widgetName;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace frc
|
} // namespace frc
|
||||||
|
|||||||
@@ -83,11 +83,18 @@ TEST(WatchdogTest, SetTimeout) {
|
|||||||
|
|
||||||
TEST(WatchdogTest, IsExpired) {
|
TEST(WatchdogTest, IsExpired) {
|
||||||
Watchdog watchdog(0.2, [] {});
|
Watchdog watchdog(0.2, [] {});
|
||||||
|
EXPECT_FALSE(watchdog.IsExpired());
|
||||||
watchdog.Enable();
|
watchdog.Enable();
|
||||||
|
|
||||||
EXPECT_FALSE(watchdog.IsExpired());
|
EXPECT_FALSE(watchdog.IsExpired());
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||||
EXPECT_TRUE(watchdog.IsExpired());
|
EXPECT_TRUE(watchdog.IsExpired());
|
||||||
|
|
||||||
|
watchdog.Disable();
|
||||||
|
EXPECT_TRUE(watchdog.IsExpired());
|
||||||
|
|
||||||
|
watchdog.Reset();
|
||||||
|
EXPECT_FALSE(watchdog.IsExpired());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WatchdogTest, Epochs) {
|
TEST(WatchdogTest, Epochs) {
|
||||||
@@ -118,7 +125,11 @@ TEST(WatchdogTest, Epochs) {
|
|||||||
EXPECT_EQ(0u, watchdogCounter) << "Watchdog triggered early";
|
EXPECT_EQ(0u, watchdogCounter) << "Watchdog triggered early";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
TEST(WatchdogTest, DISABLED_MultiWatchdog) {
|
||||||
|
#else
|
||||||
TEST(WatchdogTest, MultiWatchdog) {
|
TEST(WatchdogTest, MultiWatchdog) {
|
||||||
|
#endif
|
||||||
uint32_t watchdogCounter1 = 0;
|
uint32_t watchdogCounter1 = 0;
|
||||||
uint32_t watchdogCounter2 = 0;
|
uint32_t watchdogCounter2 = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
||||||
|
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||||
|
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||||
|
/* the project. */
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <networktables/NetworkTableEntry.h>
|
||||||
|
#include <networktables/NetworkTableInstance.h>
|
||||||
|
|
||||||
|
#include "frc/commands/InstantCommand.h"
|
||||||
|
#include "frc/shuffleboard/BuiltInWidgets.h"
|
||||||
|
#include "frc/shuffleboard/ShuffleboardInstance.h"
|
||||||
|
#include "frc/shuffleboard/ShuffleboardTab.h"
|
||||||
|
#include "frc/shuffleboard/ShuffleboardWidget.h"
|
||||||
|
#include "frc/smartdashboard/Sendable.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using namespace frc;
|
||||||
|
|
||||||
|
class ShuffleboardWidgetTest : public testing::Test {
|
||||||
|
void SetUp() override {
|
||||||
|
m_ntInstance = nt::NetworkTableInstance::Create();
|
||||||
|
m_instance = std::make_unique<detail::ShuffleboardInstance>(m_ntInstance);
|
||||||
|
m_tab = &(m_instance->GetTab("Tab"));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
nt::NetworkTableInstance m_ntInstance;
|
||||||
|
ShuffleboardTab* m_tab;
|
||||||
|
std::unique_ptr<detail::ShuffleboardInstance> m_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(ShuffleboardWidgetTest, UseBuiltInWidget) {
|
||||||
|
auto entry =
|
||||||
|
m_tab->Add("Name", "").WithWidget(BuiltInWidgets::kTextView).GetEntry();
|
||||||
|
EXPECT_EQ("/Shuffleboard/Tab/Name", entry.GetName())
|
||||||
|
<< "The widget entry has the wrong name";
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ShuffleboardWidgetTest, WithProperties) {
|
||||||
|
wpi::StringMap<std::shared_ptr<nt::Value>> properties{
|
||||||
|
std::make_pair("min", nt::Value::MakeDouble(0)),
|
||||||
|
std::make_pair("max", nt::Value::MakeDouble(1))};
|
||||||
|
auto entry =
|
||||||
|
m_tab->Add("WithProperties", "").WithProperties(properties).GetEntry();
|
||||||
|
|
||||||
|
// Update the instance to generate
|
||||||
|
// the metadata entries for the widget properties
|
||||||
|
m_instance->Update();
|
||||||
|
|
||||||
|
auto propertiesTable = m_ntInstance.GetTable(
|
||||||
|
"/Shuffleboard/.metadata/Tab/WithProperties/Properties");
|
||||||
|
|
||||||
|
EXPECT_EQ("/Shuffleboard/Tab/WithProperties", entry.GetName())
|
||||||
|
<< "The widget entry has the wrong name";
|
||||||
|
EXPECT_FLOAT_EQ(0, propertiesTable->GetEntry("min").GetDouble(-1))
|
||||||
|
<< "The 'min' property should be 0";
|
||||||
|
EXPECT_FLOAT_EQ(1, propertiesTable->GetEntry("max").GetDouble(-1))
|
||||||
|
<< "The 'max' property should be 1";
|
||||||
|
}
|
||||||
@@ -86,7 +86,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
|||||||
* never indicate that the code is ready, causing the robot to be bypassed in a match.
|
* never indicate that the code is ready, causing the robot to be bypassed in a match.
|
||||||
*/
|
*/
|
||||||
public void robotInit() {
|
public void robotInit() {
|
||||||
System.out.println("Default robotInit() method... Overload me!");
|
System.out.println("Default robotInit() method... Override me!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,7 +96,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
|||||||
* robot enters disabled mode.
|
* robot enters disabled mode.
|
||||||
*/
|
*/
|
||||||
public void disabledInit() {
|
public void disabledInit() {
|
||||||
System.out.println("Default disabledInit() method... Overload me!");
|
System.out.println("Default disabledInit() method... Override me!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,7 +106,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
|||||||
* robot enters autonomous mode.
|
* robot enters autonomous mode.
|
||||||
*/
|
*/
|
||||||
public void autonomousInit() {
|
public void autonomousInit() {
|
||||||
System.out.println("Default autonomousInit() method... Overload me!");
|
System.out.println("Default autonomousInit() method... Override me!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,7 +116,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
|||||||
* robot enters teleop mode.
|
* robot enters teleop mode.
|
||||||
*/
|
*/
|
||||||
public void teleopInit() {
|
public void teleopInit() {
|
||||||
System.out.println("Default teleopInit() method... Overload me!");
|
System.out.println("Default teleopInit() method... Override me!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,7 +127,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("PMD.JUnit4TestShouldUseTestAnnotation")
|
@SuppressWarnings("PMD.JUnit4TestShouldUseTestAnnotation")
|
||||||
public void testInit() {
|
public void testInit() {
|
||||||
System.out.println("Default testInit() method... Overload me!");
|
System.out.println("Default testInit() method... Override me!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------- Overridable periodic code ----------------- */
|
/* ----------- Overridable periodic code ----------------- */
|
||||||
@@ -139,7 +139,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
|||||||
*/
|
*/
|
||||||
public void robotPeriodic() {
|
public void robotPeriodic() {
|
||||||
if (m_rpFirstRun) {
|
if (m_rpFirstRun) {
|
||||||
System.out.println("Default robotPeriodic() method... Overload me!");
|
System.out.println("Default robotPeriodic() method... Override me!");
|
||||||
m_rpFirstRun = false;
|
m_rpFirstRun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,7 +151,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
|||||||
*/
|
*/
|
||||||
public void disabledPeriodic() {
|
public void disabledPeriodic() {
|
||||||
if (m_dpFirstRun) {
|
if (m_dpFirstRun) {
|
||||||
System.out.println("Default disabledPeriodic() method... Overload me!");
|
System.out.println("Default disabledPeriodic() method... Override me!");
|
||||||
m_dpFirstRun = false;
|
m_dpFirstRun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +163,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
|||||||
*/
|
*/
|
||||||
public void autonomousPeriodic() {
|
public void autonomousPeriodic() {
|
||||||
if (m_apFirstRun) {
|
if (m_apFirstRun) {
|
||||||
System.out.println("Default autonomousPeriodic() method... Overload me!");
|
System.out.println("Default autonomousPeriodic() method... Override me!");
|
||||||
m_apFirstRun = false;
|
m_apFirstRun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,7 +175,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
|||||||
*/
|
*/
|
||||||
public void teleopPeriodic() {
|
public void teleopPeriodic() {
|
||||||
if (m_tpFirstRun) {
|
if (m_tpFirstRun) {
|
||||||
System.out.println("Default teleopPeriodic() method... Overload me!");
|
System.out.println("Default teleopPeriodic() method... Override me!");
|
||||||
m_tpFirstRun = false;
|
m_tpFirstRun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
|||||||
@SuppressWarnings("PMD.JUnit4TestShouldUseTestAnnotation")
|
@SuppressWarnings("PMD.JUnit4TestShouldUseTestAnnotation")
|
||||||
public void testPeriodic() {
|
public void testPeriodic() {
|
||||||
if (m_tmpFirstRun) {
|
if (m_tmpFirstRun) {
|
||||||
System.out.println("Default testPeriodic() method... Overload me!");
|
System.out.println("Default testPeriodic() method... Override me!");
|
||||||
m_tmpFirstRun = false;
|
m_tmpFirstRun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -667,7 +667,7 @@ public class PIDBase extends SendableBase implements PIDInterface, PIDOutput {
|
|||||||
*
|
*
|
||||||
* @param pidSource the type of input
|
* @param pidSource the type of input
|
||||||
*/
|
*/
|
||||||
void setPIDSourceType(PIDSourceType pidSource) {
|
public void setPIDSourceType(PIDSourceType pidSource) {
|
||||||
m_pidInput.setPIDSourceType(pidSource);
|
m_pidInput.setPIDSourceType(pidSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -676,7 +676,7 @@ public class PIDBase extends SendableBase implements PIDInterface, PIDOutput {
|
|||||||
*
|
*
|
||||||
* @return the PID controller input type
|
* @return the PID controller input type
|
||||||
*/
|
*/
|
||||||
PIDSourceType getPIDSourceType() {
|
public PIDSourceType getPIDSourceType() {
|
||||||
return m_pidInput.getPIDSourceType();
|
return m_pidInput.getPIDSourceType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public class SampleRobot extends RobotBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disabled should go here. Users should overload this method to run code that should run while
|
* Disabled should go here. Users should override this method to run code that should run while
|
||||||
* the field is disabled.
|
* the field is disabled.
|
||||||
*
|
*
|
||||||
* <p>Called once each time the robot enters the disabled state.
|
* <p>Called once each time the robot enters the disabled state.
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class Ultrasonic extends SendableBase implements PIDSource {
|
|||||||
// ultrasonic sensor list
|
// ultrasonic sensor list
|
||||||
private static final List<Ultrasonic> m_sensors = new ArrayList<>();
|
private static final List<Ultrasonic> m_sensors = new ArrayList<>();
|
||||||
// automatic round robin mode
|
// automatic round robin mode
|
||||||
private static boolean m_automaticEnabled;
|
private static volatile boolean m_automaticEnabled;
|
||||||
private DigitalInput m_echoChannel;
|
private DigitalInput m_echoChannel;
|
||||||
private DigitalOutput m_pingChannel;
|
private DigitalOutput m_pingChannel;
|
||||||
private boolean m_allocatedChannels;
|
private boolean m_allocatedChannels;
|
||||||
@@ -70,30 +70,18 @@ public class Ultrasonic extends SendableBase implements PIDSource {
|
|||||||
private static class UltrasonicChecker extends Thread {
|
private static class UltrasonicChecker extends Thread {
|
||||||
@Override
|
@Override
|
||||||
public synchronized void run() {
|
public synchronized void run() {
|
||||||
int sensorIndex = 0;
|
|
||||||
Ultrasonic ultrasonic;
|
|
||||||
while (m_automaticEnabled) {
|
while (m_automaticEnabled) {
|
||||||
//lock list to ensure deletion doesn't occur between empty check and retrieving sensor
|
for (Ultrasonic sensor: m_sensors) {
|
||||||
synchronized (m_sensors) {
|
if (!m_automaticEnabled) {
|
||||||
if (m_sensors.isEmpty()) {
|
break;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (sensorIndex >= m_sensors.size()) {
|
|
||||||
sensorIndex = m_sensors.size() - 1;
|
|
||||||
}
|
|
||||||
ultrasonic = m_sensors.get(sensorIndex);
|
|
||||||
}
|
|
||||||
if (ultrasonic.isEnabled()) {
|
|
||||||
// Do the ping
|
|
||||||
ultrasonic.m_pingChannel.pulse(kPingTime);
|
|
||||||
}
|
|
||||||
if (sensorIndex < m_sensors.size()) {
|
|
||||||
sensorIndex++;
|
|
||||||
} else {
|
|
||||||
sensorIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer.delay(.1); // wait for ping to return
|
if (sensor.isEnabled()) {
|
||||||
|
sensor.m_pingChannel.pulse(kPingTime); // do the ping
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer.delay(0.1); // wait for ping to return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,8 +195,6 @@ public class Watchdog implements Closeable, Comparable<Watchdog> {
|
|||||||
public void disable() {
|
public void disable() {
|
||||||
m_queueMutex.lock();
|
m_queueMutex.lock();
|
||||||
try {
|
try {
|
||||||
m_isExpired = false;
|
|
||||||
|
|
||||||
m_watchdogs.remove(this);
|
m_watchdogs.remove(this);
|
||||||
m_schedulerWaiter.signalAll();
|
m_schedulerWaiter.signalAll();
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ class WatchdogTest {
|
|||||||
void isExpiredTest() {
|
void isExpiredTest() {
|
||||||
final Watchdog watchdog = new Watchdog(0.2, () -> {
|
final Watchdog watchdog = new Watchdog(0.2, () -> {
|
||||||
});
|
});
|
||||||
|
assertFalse(watchdog.isExpired());
|
||||||
watchdog.enable();
|
watchdog.enable();
|
||||||
|
|
||||||
assertFalse(watchdog.isExpired());
|
assertFalse(watchdog.isExpired());
|
||||||
@@ -130,6 +131,12 @@ class WatchdogTest {
|
|||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
assertTrue(watchdog.isExpired());
|
assertTrue(watchdog.isExpired());
|
||||||
|
|
||||||
|
watchdog.disable();
|
||||||
|
assertTrue(watchdog.isExpired());
|
||||||
|
|
||||||
|
watchdog.reset();
|
||||||
|
assertFalse(watchdog.isExpired());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -25,8 +25,27 @@ std::shared_ptr<Pipe> Pipe::Create(Loop& loop, bool ipc) {
|
|||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Pipe::Reuse(std::function<void()> callback, bool ipc) {
|
||||||
|
if (IsClosing()) return;
|
||||||
|
if (!m_reuseData) m_reuseData = std::make_unique<ReuseData>();
|
||||||
|
m_reuseData->callback = callback;
|
||||||
|
m_reuseData->ipc = ipc;
|
||||||
|
uv_close(GetRawHandle(), [](uv_handle_t* handle) {
|
||||||
|
Pipe& h = *static_cast<Pipe*>(handle->data);
|
||||||
|
if (!h.m_reuseData) return;
|
||||||
|
auto data = std::move(h.m_reuseData);
|
||||||
|
auto err =
|
||||||
|
uv_pipe_init(h.GetLoopRef().GetRaw(), h.GetRaw(), data->ipc ? 1 : 0);
|
||||||
|
if (err < 0) {
|
||||||
|
h.ReportError(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data->callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Pipe> Pipe::Accept() {
|
std::shared_ptr<Pipe> Pipe::Accept() {
|
||||||
auto client = Create(GetLoopRef());
|
auto client = Create(GetLoopRef(), GetRaw()->ipc);
|
||||||
if (!client) return nullptr;
|
if (!client) return nullptr;
|
||||||
if (!Accept(client)) {
|
if (!Accept(client)) {
|
||||||
client->Release();
|
client->Release();
|
||||||
|
|||||||
@@ -56,6 +56,18 @@ class Pipe final : public NetworkStreamImpl<Pipe, uv_pipe_t> {
|
|||||||
return Create(*loop, ipc);
|
return Create(*loop, ipc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reuse this handle. This closes the handle, and after the close completes,
|
||||||
|
* reinitializes it (identically to Create) and calls the provided callback.
|
||||||
|
* Unlike Close(), it does NOT emit the closed signal, however, IsClosing()
|
||||||
|
* will return true until the callback is called. This does nothing if
|
||||||
|
* IsClosing() is true (e.g. if Close() was called).
|
||||||
|
*
|
||||||
|
* @param ipc IPC
|
||||||
|
* @param callback Callback
|
||||||
|
*/
|
||||||
|
void Reuse(std::function<void()> callback, bool ipc = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accept incoming connection.
|
* Accept incoming connection.
|
||||||
*
|
*
|
||||||
@@ -176,6 +188,12 @@ class Pipe final : public NetworkStreamImpl<Pipe, uv_pipe_t> {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Pipe* DoAccept() override;
|
Pipe* DoAccept() override;
|
||||||
|
|
||||||
|
struct ReuseData {
|
||||||
|
std::function<void()> callback;
|
||||||
|
bool ipc;
|
||||||
|
};
|
||||||
|
std::unique_ptr<ReuseData> m_reuseData;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user