mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01: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
|
||||
- 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
|
||||
- [ARM Compiler Toolchain](http://first.wpi.edu/FRC/roborio/toolchains/)
|
||||
* Note that for 2017-2018 and beyond, you will need version 5 or greater of GCC
|
||||
- [ARM Compiler Toolchain](https://github.com/wpilibsuite/toolchain-builder/releases)
|
||||
* 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)
|
||||
|
||||
## 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.
|
||||
|
||||
CMake is also supported for building. See [README-CMAKE.md](README-CMAKE.md).
|
||||
|
||||
## 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.
|
||||
- beta - Publishes to ~/releases/maven/beta.
|
||||
- stable - Publishes to ~/releases/maven/stable.
|
||||
- release - Publishes to ~/releases/maven/release.
|
||||
|
||||
The following maven targets a published by this task:
|
||||
|
||||
- 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.
|
||||
The maven artifacts are described in [MavenArtifacts.md](MavenArtifacts.md)
|
||||
|
||||
## Structure and Organization
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ public final class CameraServer {
|
||||
private final Map<String, VideoSource> m_sources;
|
||||
private final Map<String, VideoSink> m_sinks;
|
||||
private final Map<Integer, NetworkTable> m_tables; // indexed by source handle
|
||||
// source handle indexed by sink handle
|
||||
private final Map<Integer, Integer> m_fixedSources;
|
||||
private final NetworkTable m_publishTable;
|
||||
private final VideoListener m_videoListener; //NOPMD
|
||||
private final int m_tableListener; //NOPMD
|
||||
@@ -157,14 +159,20 @@ public final class CameraServer {
|
||||
return values;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP"})
|
||||
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP", "PMD.CyclomaticComplexity"})
|
||||
private synchronized void updateStreamValues() {
|
||||
// Over all the sinks...
|
||||
for (VideoSink i : m_sinks.values()) {
|
||||
int sink = i.getHandle();
|
||||
|
||||
// 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) {
|
||||
continue;
|
||||
}
|
||||
@@ -295,6 +303,7 @@ public final class CameraServer {
|
||||
m_defaultUsbDevice = new AtomicInteger();
|
||||
m_sources = new Hashtable<>();
|
||||
m_sinks = new Hashtable<>();
|
||||
m_fixedSources = new Hashtable<>();
|
||||
m_tables = new Hashtable<>();
|
||||
m_publishTable = NetworkTableInstance.getDefault().getTable(kPublishName);
|
||||
m_nextPort = kBasePort;
|
||||
@@ -537,10 +546,11 @@ public final class CameraServer {
|
||||
*
|
||||
* @param camera Camera
|
||||
*/
|
||||
public void startAutomaticCapture(VideoSource camera) {
|
||||
public MjpegServer startAutomaticCapture(VideoSource camera) {
|
||||
addCamera(camera);
|
||||
VideoSink server = addServer("serve_" + camera.getName());
|
||||
MjpegServer server = addServer("serve_" + camera.getName());
|
||||
server.setSource(camera);
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -595,6 +605,21 @@ public final class CameraServer {
|
||||
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 images from the camera for image processing on the roboRIO.
|
||||
|
||||
@@ -33,10 +33,11 @@ struct CameraServer::Impl {
|
||||
void UpdateStreamValues();
|
||||
|
||||
wpi::mutex m_mutex;
|
||||
std::atomic<int> m_defaultUsbDevice;
|
||||
std::atomic<int> m_defaultUsbDevice{0};
|
||||
std::string m_primarySourceName;
|
||||
wpi::StringMap<cs::VideoSource> m_sources;
|
||||
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;
|
||||
std::shared_ptr<nt::NetworkTable> m_publishTable;
|
||||
cs::VideoListener m_videoListener;
|
||||
@@ -156,7 +157,8 @@ void CameraServer::Impl::UpdateStreamValues() {
|
||||
CS_Sink sink = i.second.GetHandle();
|
||||
|
||||
// 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;
|
||||
auto table = m_tables.lookup(source);
|
||||
if (table) {
|
||||
@@ -538,10 +540,21 @@ cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
||||
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);
|
||||
auto server = AddServer(wpi::Twine("serve_") + camera.GetName());
|
||||
server.SetSource(camera);
|
||||
return server;
|
||||
}
|
||||
|
||||
cs::CvSink CameraServer::GetVideo() {
|
||||
|
||||
@@ -82,7 +82,7 @@ class CameraServer {
|
||||
*
|
||||
* @param camera Camera
|
||||
*/
|
||||
void StartAutomaticCapture(const cs::VideoSource& camera);
|
||||
cs::MjpegServer StartAutomaticCapture(const cs::VideoSource& camera);
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
@@ -173,6 +173,14 @@ class CameraServer {
|
||||
cs::AxisCamera AddAxisCamera(const wpi::Twine& name,
|
||||
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 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 int getSinkProperty(int sink, String name);
|
||||
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 int getSinkSourceProperty(int sink, String name);
|
||||
public static native int getSinkSource(int sink);
|
||||
|
||||
@@ -60,7 +60,7 @@ public class MjpegServer extends VideoSink {
|
||||
* @param width width, 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, "height"), height);
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public class MjpegServer extends VideoSink {
|
||||
*
|
||||
* @param fps FPS, 0 for unspecified
|
||||
*/
|
||||
void setFPS(int fps) {
|
||||
public void setFPS(int 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
|
||||
*/
|
||||
void setCompression(int quality) {
|
||||
public void setCompression(int quality) {
|
||||
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "compression"),
|
||||
quality);
|
||||
}
|
||||
@@ -97,7 +97,7 @@ public class MjpegServer extends VideoSink {
|
||||
*
|
||||
* @param quality JPEG compression quality (0-100)
|
||||
*/
|
||||
void setDefaultCompression(int quality) {
|
||||
public void setDefaultCompression(int quality) {
|
||||
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "default_compression"),
|
||||
quality);
|
||||
}
|
||||
|
||||
@@ -133,6 +133,38 @@ public class VideoSink implements AutoCloseable {
|
||||
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
|
||||
* 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() {
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
m_source->EnableSink();
|
||||
if (m_source) m_source->EnableSink();
|
||||
m_streaming = true;
|
||||
}
|
||||
|
||||
void StopStream() {
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
m_source->DisableSink();
|
||||
if (m_source) m_source->DisableSink();
|
||||
m_streaming = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
|
||||
#include "PropertyContainer.h"
|
||||
|
||||
#include <wpi/Logger.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/json.h>
|
||||
|
||||
using namespace cs;
|
||||
|
||||
int PropertyContainer::GetPropertyIndex(const wpi::Twine& name) const {
|
||||
@@ -204,3 +209,74 @@ bool PropertyContainer::CacheProperties(CS_Status* status) const {
|
||||
m_properties_cached = 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 "cscore_cpp.h"
|
||||
|
||||
namespace wpi {
|
||||
class Logger;
|
||||
class json;
|
||||
} // namespace wpi
|
||||
|
||||
namespace cs {
|
||||
|
||||
class PropertyContainer {
|
||||
@@ -50,6 +55,10 @@ class PropertyContainer {
|
||||
std::vector<std::string> GetEnumPropertyChoices(int property,
|
||||
CS_Status* status) const;
|
||||
|
||||
bool SetPropertiesJson(const wpi::json& config, wpi::Logger& logger,
|
||||
wpi::StringRef logName, CS_Status* status);
|
||||
wpi::json GetPropertiesJsonObject(CS_Status* status);
|
||||
|
||||
protected:
|
||||
// Get a property; must be called with m_mutex held.
|
||||
PropertyImpl* GetProperty(int property) {
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include "SinkImpl.h"
|
||||
|
||||
#include <wpi/json.h>
|
||||
|
||||
#include "Instance.h"
|
||||
#include "Notifier.h"
|
||||
#include "SourceImpl.h"
|
||||
@@ -102,6 +104,43 @@ wpi::StringRef SinkImpl::GetError(wpi::SmallVectorImpl<char>& buf) const {
|
||||
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) {
|
||||
m_notifier.NotifySinkProperty(*this, CS_SINK_PROPERTY_CREATED, prop.name,
|
||||
propIndex, prop.propKind, prop.value,
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
|
||||
#include "SourceImpl.h"
|
||||
|
||||
namespace wpi {
|
||||
class json;
|
||||
} // namespace wpi
|
||||
|
||||
namespace cs {
|
||||
|
||||
class Frame;
|
||||
@@ -51,6 +55,11 @@ class SinkImpl : public PropertyContainer {
|
||||
std::string GetError() 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:
|
||||
// PropertyContainer implementation
|
||||
void NotifyPropertyCreated(int propIndex, PropertyImpl& prop) override;
|
||||
|
||||
@@ -319,38 +319,8 @@ bool SourceImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
|
||||
}
|
||||
|
||||
// properties
|
||||
if (config.count("properties") != 0) {
|
||||
for (auto&& prop : config.at("properties")) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.count("properties") != 0)
|
||||
SetPropertiesJson(config.at("properties"), m_logger, GetName(), status);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -401,28 +371,7 @@ wpi::json SourceImpl::GetConfigJsonObject(CS_Status* status) {
|
||||
// TODO: output brightness, white balance, and exposure?
|
||||
|
||||
// properties
|
||||
wpi::json props;
|
||||
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);
|
||||
}
|
||||
wpi::json props = GetPropertiesJsonObject(status);
|
||||
if (props.is_array()) j.emplace("properties", props);
|
||||
|
||||
return j;
|
||||
|
||||
@@ -277,6 +277,15 @@ CS_Property* CS_EnumerateSinkProperties(CS_Sink sink, int* count,
|
||||
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) {
|
||||
return cs::SetSinkSource(sink, source, status);
|
||||
}
|
||||
|
||||
@@ -564,6 +564,43 @@ wpi::ArrayRef<CS_Property> EnumerateSinkProperties(
|
||||
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) {
|
||||
auto data = Instance::GetInstance().GetSink(sink);
|
||||
if (!data) {
|
||||
|
||||
@@ -16,6 +16,11 @@ wpi::json VideoSource::GetConfigJsonObject() const {
|
||||
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 {
|
||||
wpi::SmallVector<CS_Property, 32> handles_buf;
|
||||
CS_Status status = 0;
|
||||
|
||||
@@ -1292,6 +1292,36 @@ Java_edu_wpi_cscore_CameraServerJNI_enumerateSinkProperties
|
||||
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
|
||||
* 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);
|
||||
CS_Property CS_GetSinkSourceProperty(CS_Sink sink, const char* name,
|
||||
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_Sink CS_CopySink(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);
|
||||
CS_Property GetSinkSourceProperty(CS_Sink sink, const wpi::Twine& name,
|
||||
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_Sink CopySink(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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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)};
|
||||
}
|
||||
|
||||
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,
|
||||
const wpi::Twine& listenAddress, int port) {
|
||||
m_handle = CreateMjpegServer(name, listenAddress, port, &m_status);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <wpi/STLExtras.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#include "UsbUtil.h"
|
||||
|
||||
@@ -151,6 +152,9 @@ UsbCameraProperty::UsbCameraProperty(const struct v4l2_query_ext_ctrl& ctrl)
|
||||
propKind = CS_PROP_BOOLEAN;
|
||||
break;
|
||||
case V4L2_CTRL_TYPE_INTEGER_MENU:
|
||||
propKind = CS_PROP_ENUM;
|
||||
intMenu = true;
|
||||
break;
|
||||
case V4L2_CTRL_TYPE_MENU:
|
||||
propKind = CS_PROP_ENUM;
|
||||
break;
|
||||
@@ -243,7 +247,12 @@ std::unique_ptr<UsbCameraProperty> UsbCameraProperty::DeviceQuery(int fd,
|
||||
for (int i = prop->minimum; i <= prop->maximum; ++i) {
|
||||
qmenu.index = static_cast<__u32>(i);
|
||||
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
|
||||
int type{0}; // implementation type, not CS_PropertyKind!
|
||||
|
||||
// If the enum property is integer rather than string
|
||||
bool intMenu{false};
|
||||
};
|
||||
|
||||
} // namespace cs
|
||||
|
||||
@@ -5,27 +5,12 @@
|
||||
/* 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 <iostream>
|
||||
|
||||
#include "cscore_cpp.h"
|
||||
|
||||
#pragma comment(lib, "IPHLPAPI.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 {
|
||||
|
||||
std::vector<std::string> GetNetworkInterfaces() {
|
||||
@@ -40,80 +25,18 @@ std::vector<std::string> GetNetworkInterfaces() {
|
||||
|
||||
for (int i = 0; i < counts; i++) {
|
||||
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,
|
||||
sizeof(ip) - 1);
|
||||
ip[49] = '\0';
|
||||
std::cout << ip << std::endl;
|
||||
InetNtop(PF_INET, &(adrs[i].address.address4.sin_addr.s_addr), ip,
|
||||
sizeof(ip) - 1);
|
||||
ip[49] = '\0';
|
||||
std::cout << ip << std::endl;
|
||||
addresses.emplace_back(std::string{ip});
|
||||
}
|
||||
|
||||
uv_free_interface_addresses(adrs, counts);
|
||||
|
||||
std::cout << "finished\n";
|
||||
|
||||
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
|
||||
|
||||
@@ -450,6 +450,15 @@ bool UsbCameraImpl::DeviceConnect() {
|
||||
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) {
|
||||
SDEBUG3("caching properties");
|
||||
DeviceCacheProperties();
|
||||
@@ -488,21 +497,32 @@ bool UsbCameraImpl::CacheProperties(CS_Status* status) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void UsbCameraImpl::DeviceAddProperty(const wpi::Twine& name_,
|
||||
tagVideoProcAmpProperty tag,
|
||||
IAMVideoProcAmp* pProcAmp) {
|
||||
template <typename TagProperty, typename IAM>
|
||||
void UsbCameraImpl::DeviceAddProperty(const wpi::Twine& name_, TagProperty tag,
|
||||
IAM* pProcAmp) {
|
||||
// First see if properties exist
|
||||
bool isValid = false;
|
||||
auto property = std::make_unique<UsbCameraProperty>(name_, tag, false,
|
||||
pProcAmp, &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) \
|
||||
DeviceAddProperty(#val, VideoProcAmp_##val, pProcAmp);
|
||||
|
||||
#define CREATECONTROLPROPERTY(val) \
|
||||
DeviceAddProperty(#val, CameraControl_##val, pCamControl);
|
||||
|
||||
void UsbCameraImpl::DeviceCacheProperties() {
|
||||
if (!m_sourceReader) return;
|
||||
|
||||
@@ -523,6 +543,21 @@ void UsbCameraImpl::DeviceCacheProperties() {
|
||||
CREATEPROPERTY(Gain)
|
||||
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,
|
||||
@@ -538,7 +573,7 @@ int UsbCameraImpl::PercentageToRaw(const UsbCameraProperty& rawProp,
|
||||
}
|
||||
|
||||
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
|
||||
// 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
|
||||
@@ -707,16 +742,7 @@ CS_StatusValue UsbCameraImpl::DeviceCmdSetProperty(
|
||||
if (!prop->device) {
|
||||
if (prop->id == kPropConnectVerboseId) m_connectVerbose = value;
|
||||
} else {
|
||||
IAMVideoProcAmp* pProcAmp = NULL;
|
||||
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 {
|
||||
if (!prop->DeviceSet(lock, m_sourceReader.Get())) {
|
||||
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
|
||||
// GetMessagePump();
|
||||
|
||||
ComPtr<IMFMediaSource> ppSource;
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
|
||||
ComPtr<IMFMediaSource> pSource;
|
||||
ComPtr<IMFAttributes> pAttributes;
|
||||
IMFActivate** ppDevices = nullptr;
|
||||
|
||||
@@ -988,7 +1012,6 @@ done:
|
||||
}
|
||||
}
|
||||
CoTaskMemFree(ppDevices);
|
||||
pSource.Reset();
|
||||
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 */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
@@ -132,11 +132,12 @@ class UsbCameraImpl : public SourceImpl,
|
||||
CS_StatusValue DeviceSetMode();
|
||||
void DeviceCacheMode();
|
||||
void DeviceCacheProperty(std::unique_ptr<UsbCameraProperty> rawProp,
|
||||
IAMVideoProcAmp* pProcAmp);
|
||||
IMFSourceReader* sourceReader);
|
||||
void DeviceCacheProperties();
|
||||
void DeviceCacheVideoModes();
|
||||
void DeviceAddProperty(const wpi::Twine& name_, tagVideoProcAmpProperty tag,
|
||||
IAMVideoProcAmp* pProcAmp);
|
||||
template <typename TagProperty, typename IAM>
|
||||
void DeviceAddProperty(const wpi::Twine& name_, TagProperty tag,
|
||||
IAM* pProcAmp);
|
||||
|
||||
ComPtr<IMFMediaType> DeviceCheckModeValid(const VideoMode& toCheck);
|
||||
|
||||
|
||||
@@ -7,13 +7,16 @@
|
||||
|
||||
#include "UsbCameraProperty.h"
|
||||
|
||||
#include "ComPtr.h"
|
||||
|
||||
using namespace cs;
|
||||
|
||||
UsbCameraProperty::UsbCameraProperty(const wpi::Twine& name_,
|
||||
tagVideoProcAmpProperty tag, bool autoProp,
|
||||
IAMVideoProcAmp* pProcAmp, bool* isValid)
|
||||
: PropertyImpl{autoProp ? name_ + "_auto" : name_} {
|
||||
this->tag = tag;
|
||||
this->tagVideoProc = tag;
|
||||
this->isControlProperty = false;
|
||||
this->isAutoProp = autoProp;
|
||||
long paramVal, paramFlag; // NOLINT(runtime/int)
|
||||
HRESULT hr;
|
||||
@@ -42,7 +45,7 @@ bool UsbCameraProperty::DeviceGet(std::unique_lock<wpi::mutex>& lock,
|
||||
|
||||
lock.unlock();
|
||||
long newValue = 0, paramFlag = 0; // NOLINT(runtime/int)
|
||||
if (SUCCEEDED(pProcAmp->Get(tag, &newValue, ¶mFlag))) {
|
||||
if (SUCCEEDED(pProcAmp->Get(tagVideoProc, &newValue, ¶mFlag))) {
|
||||
lock.lock();
|
||||
value = newValue;
|
||||
return true;
|
||||
@@ -60,10 +63,127 @@ bool UsbCameraProperty::DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
||||
if (!pProcAmp) return true;
|
||||
|
||||
lock.unlock();
|
||||
if (SUCCEEDED(pProcAmp->Set(tag, newValue, VideoProcAmp_Flags_Manual))) {
|
||||
if (SUCCEEDED(
|
||||
pProcAmp->Set(tagVideoProc, newValue, VideoProcAmp_Flags_Manual))) {
|
||||
lock.lock();
|
||||
return true;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#include <mfapi.h>
|
||||
#include <mfidl.h>
|
||||
#include <mfreadwrite.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <Dshow.h>
|
||||
@@ -49,16 +53,23 @@ class UsbCameraProperty : public PropertyImpl {
|
||||
UsbCameraProperty(const wpi::Twine& name_, tagVideoProcAmpProperty tag,
|
||||
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,
|
||||
IAMVideoProcAmp* pProcAmp) const;
|
||||
bool DeviceSet(std::unique_lock<wpi::mutex>& lock, IAMVideoProcAmp* pProcAmp,
|
||||
int newValue) const;
|
||||
IMFSourceReader* sourceReader) const;
|
||||
bool DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
||||
IMFSourceReader* sourceReader, int newValue) const;
|
||||
|
||||
// If this is a device (rather than software) property
|
||||
bool device{true};
|
||||
bool isAutoProp{true};
|
||||
tagVideoProcAmpProperty tag;
|
||||
|
||||
bool isControlProperty{false};
|
||||
tagVideoProcAmpProperty tagVideoProc;
|
||||
tagCameraControlProperty tagCameraControl;
|
||||
|
||||
// If this is a percentage (rather than raw) property
|
||||
bool percentage{false};
|
||||
@@ -68,5 +79,19 @@ class UsbCameraProperty : public PropertyImpl {
|
||||
|
||||
unsigned id{0}; // implementation-level id
|
||||
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
|
||||
|
||||
@@ -112,6 +112,19 @@ task generateJavaDocs(type: Javadoc) {
|
||||
source configurations.javaSource.collect { zipTree(it) }
|
||||
include '**/*.java'
|
||||
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) {
|
||||
|
||||
@@ -26,7 +26,7 @@ file(GLOB
|
||||
hal_shared_native_src src/main/native/cpp/cpp/*.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/MockData/*.cpp)
|
||||
hal_sim_native_src src/main/native/sim/mockdata/*.cpp)
|
||||
add_library(hal ${hal_shared_native_src})
|
||||
set_target_properties(hal PROPERTIES DEBUG_POSTFIX "d")
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
#include "HALInitializer.h"
|
||||
#include "PortsInternal.h"
|
||||
#include "hal/CANAPI.h"
|
||||
@@ -106,9 +108,16 @@ union PdpStatusEnergy {
|
||||
} bits;
|
||||
};
|
||||
|
||||
static wpi::mutex pdpHandleMutex;
|
||||
static HAL_PDPHandle pdpHandles[kNumPDPModules];
|
||||
|
||||
namespace hal {
|
||||
namespace init {
|
||||
void InitializePDP() {}
|
||||
void InitializePDP() {
|
||||
for (int i = 0; i < kNumPDPModules; i++) {
|
||||
pdpHandles[i] = HAL_kInvalidHandle;
|
||||
}
|
||||
}
|
||||
} // namespace init
|
||||
} // namespace hal
|
||||
|
||||
@@ -121,6 +130,13 @@ HAL_PDPHandle HAL_InitializePDP(int32_t module, int32_t* status) {
|
||||
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);
|
||||
|
||||
if (*status != 0) {
|
||||
@@ -128,10 +144,21 @@ HAL_PDPHandle HAL_InitializePDP(int32_t module, int32_t* status) {
|
||||
return HAL_kInvalidHandle;
|
||||
}
|
||||
|
||||
pdpHandles[module] = 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) {
|
||||
return module < kNumPDPModules && module >= 0;
|
||||
|
||||
@@ -127,7 +127,7 @@ void ReportError(JNIEnv* env, int32_t status, bool doThrow) {
|
||||
ThrowUncleanStatusException(env, buf.c_str(), status);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,10 +109,6 @@ static wpi::StringRef UnescapeString(wpi::StringRef source,
|
||||
continue;
|
||||
}
|
||||
switch (*++s) {
|
||||
case '\\':
|
||||
case '"':
|
||||
buf.push_back(s[-1]);
|
||||
break;
|
||||
case 't':
|
||||
buf.push_back('\t');
|
||||
break;
|
||||
@@ -133,7 +129,7 @@ static wpi::StringRef UnescapeString(wpi::StringRef source,
|
||||
break;
|
||||
}
|
||||
default:
|
||||
buf.push_back(s[-1]);
|
||||
buf.push_back(*s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ class StorageTestPersistent : public StorageTestEmpty {
|
||||
storage.SetEntryTypeValue("string/normal", Value::MakeString("hello"));
|
||||
storage.SetEntryTypeValue("string/special",
|
||||
Value::MakeString(StringRef("\0\3\5\n", 4)));
|
||||
storage.SetEntryTypeValue("string/quoted", Value::MakeString("\"a\""));
|
||||
storage.SetEntryTypeValue("raw/empty", Value::MakeRaw(""));
|
||||
storage.SetEntryTypeValue("raw/normal", Value::MakeRaw("hello"));
|
||||
storage.SetEntryTypeValue("raw/special",
|
||||
@@ -599,6 +600,8 @@ TEST_P(StorageTestPersistent, SavePersistent) {
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("string \"string/normal\"=\"hello\"", line);
|
||||
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);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("array string \"stringarr/empty\"=", line);
|
||||
@@ -787,18 +790,19 @@ TEST_P(StorageTestEmpty, LoadPersistent) {
|
||||
in += "string \"string/empty\"=\"\"\n";
|
||||
in += "string \"string/normal\"=\"hello\"\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/one\"=\"hello\"\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,
|
||||
NotifyEntry(_, _, _, NT_NOTIFY_NEW | NT_NOTIFY_LOCAL, UINT_MAX))
|
||||
.Times(22);
|
||||
.Times(23);
|
||||
|
||||
wpi::raw_mem_istream iss(in);
|
||||
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(false),
|
||||
@@ -811,6 +815,8 @@ TEST_P(StorageTestEmpty, LoadPersistent) {
|
||||
*storage.GetEntryValue("string/normal"));
|
||||
EXPECT_EQ(*Value::MakeString(StringRef("\0\3\5\n", 4)),
|
||||
*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("hello"), *storage.GetEntryValue("raw/normal"));
|
||||
EXPECT_EQ(*Value::MakeRaw(StringRef("\0\3\5\n", 4)),
|
||||
|
||||
@@ -26,6 +26,7 @@ DSCommPacket::DSCommPacket() {
|
||||
i.ResetTcp();
|
||||
i.ResetUdp();
|
||||
}
|
||||
matchInfo.gameSpecificMessageSize = 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
@@ -95,7 +96,7 @@ void DSCommPacket::ReadJoystickTag(wpi::ArrayRef<uint8_t> dataInput,
|
||||
int numBytes = (buttonCount + 7) / 8;
|
||||
stick.buttons.buttons = 0;
|
||||
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;
|
||||
|
||||
|
||||
@@ -66,13 +66,21 @@ TEST_F(DSCommPacketTest, BlankJoystickTag) {
|
||||
|
||||
TEST_F(DSCommPacketTest, MainJoystickTag) {
|
||||
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
|
||||
uint8_t arr[5 + 4 + 2 + 6] = {// Size, Tag
|
||||
16, 12,
|
||||
// Axes
|
||||
4, 0x9C, 0xCE, 0, 75,
|
||||
// Buttons
|
||||
12, 0xFF, 0x0F,
|
||||
// Buttons (LSB 0)
|
||||
12, _button_bytes[0], _button_bytes[1],
|
||||
// POVs
|
||||
3, 0, 50, 0, 100, 0x0F, 0x00};
|
||||
|
||||
@@ -80,6 +88,11 @@ TEST_F(DSCommPacketTest, MainJoystickTag) {
|
||||
ASSERT_EQ(data.axes.count, 4);
|
||||
ASSERT_EQ(data.povs.count, 3);
|
||||
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(); }) {}
|
||||
|
||||
void IterativeRobotBase::RobotInit() {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
}
|
||||
|
||||
void IterativeRobotBase::DisabledInit() {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
}
|
||||
|
||||
void IterativeRobotBase::AutonomousInit() {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
}
|
||||
|
||||
void IterativeRobotBase::TeleopInit() {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
}
|
||||
|
||||
void IterativeRobotBase::TestInit() {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
}
|
||||
|
||||
void IterativeRobotBase::RobotPeriodic() {
|
||||
static bool firstRun = true;
|
||||
if (firstRun) {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
firstRun = false;
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ void IterativeRobotBase::RobotPeriodic() {
|
||||
void IterativeRobotBase::DisabledPeriodic() {
|
||||
static bool firstRun = true;
|
||||
if (firstRun) {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
firstRun = false;
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ void IterativeRobotBase::DisabledPeriodic() {
|
||||
void IterativeRobotBase::AutonomousPeriodic() {
|
||||
static bool firstRun = true;
|
||||
if (firstRun) {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
firstRun = false;
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ void IterativeRobotBase::AutonomousPeriodic() {
|
||||
void IterativeRobotBase::TeleopPeriodic() {
|
||||
static bool firstRun = true;
|
||||
if (firstRun) {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
firstRun = false;
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ void IterativeRobotBase::TeleopPeriodic() {
|
||||
void IterativeRobotBase::TestPeriodic() {
|
||||
static bool firstRun = true;
|
||||
if (firstRun) {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
firstRun = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,23 +59,23 @@ void SampleRobot::StartCompetition() {
|
||||
}
|
||||
|
||||
void SampleRobot::RobotInit() {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
}
|
||||
|
||||
void SampleRobot::Disabled() {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
}
|
||||
|
||||
void SampleRobot::Autonomous() {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
}
|
||||
|
||||
void SampleRobot::OperatorControl() {
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Overload me!\n";
|
||||
wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
@@ -18,7 +18,7 @@ constexpr std::chrono::milliseconds Watchdog::kMinPrintPeriod;
|
||||
class Watchdog::Thread : public wpi::SafeThread {
|
||||
public:
|
||||
template <typename T>
|
||||
struct DerefGreater : public std::binary_function<T, T, bool> {
|
||||
struct DerefGreater {
|
||||
constexpr bool operator()(const T& lhs, const T& rhs) const {
|
||||
return *lhs > *rhs;
|
||||
}
|
||||
@@ -160,8 +160,6 @@ void Watchdog::Disable() {
|
||||
auto thr = m_owner->GetThread();
|
||||
if (!thr) return;
|
||||
|
||||
m_isExpired = false;
|
||||
|
||||
thr->m_watchdogs.remove(this);
|
||||
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;
|
||||
|
||||
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)
|
||||
: ShuffleboardValue(title) {}
|
||||
|
||||
@@ -26,6 +32,11 @@ ShuffleboardContainer::GetComponents() const {
|
||||
return m_components;
|
||||
}
|
||||
|
||||
ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& title,
|
||||
BuiltInLayouts type) {
|
||||
return GetLayout(title, GetStringFromBuiltInLayout(type));
|
||||
}
|
||||
|
||||
ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& title,
|
||||
const LayoutType& type) {
|
||||
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"
|
||||
|
||||
namespace frc {
|
||||
/** WPILib FileSystem namespace */
|
||||
namespace filesystem {
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,8 +19,7 @@ namespace frc {
|
||||
* .GetLayout(BuiltinLayouts::kList, "My List");
|
||||
* }</pre>
|
||||
*/
|
||||
class BuiltInLayouts {
|
||||
public:
|
||||
enum class BuiltInLayouts {
|
||||
/**
|
||||
* 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>
|
||||
@@ -30,7 +29,7 @@ class BuiltInLayouts {
|
||||
* {@code ["TOP", "LEFT", "BOTTOM", "RIGHT", "HIDDEN"}</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
static const LayoutType kList;
|
||||
kList,
|
||||
|
||||
/**
|
||||
* Groups components in an <i>n</i> x <i>m</i> grid. Grid layouts default to
|
||||
@@ -47,7 +46,7 @@ class BuiltInLayouts {
|
||||
* </tr>
|
||||
* </table>
|
||||
*/
|
||||
static const LayoutType kGrid;
|
||||
kGrid
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
|
||||
@@ -25,8 +25,7 @@ namespace frc {
|
||||
* <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.
|
||||
*/
|
||||
class BuiltInWidgets {
|
||||
public:
|
||||
enum class BuiltInWidgets {
|
||||
/**
|
||||
* Displays a value with a simple text field.
|
||||
* <br>Supported types:
|
||||
@@ -37,7 +36,7 @@ class BuiltInWidgets {
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
static const WidgetType kTextView;
|
||||
kTextView,
|
||||
/**
|
||||
* Displays a number with a controllable slider.
|
||||
* <br>Supported types:
|
||||
@@ -54,7 +53,7 @@ class BuiltInWidgets {
|
||||
* slider by with the arrow keys</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType kNumberSlider;
|
||||
kNumberSlider,
|
||||
/**
|
||||
* Displays a number with a view-only bar.
|
||||
* <br>Supported types:
|
||||
@@ -71,7 +70,7 @@ class BuiltInWidgets {
|
||||
* of the bar</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType kNumberBar;
|
||||
kNumberBar,
|
||||
/**
|
||||
* Displays a number with a view-only dial. Displayed values are rounded to
|
||||
* the nearest integer. <br>Supported types: <ul> <li>Number</li>
|
||||
@@ -86,7 +85,7 @@ class BuiltInWidgets {
|
||||
* value as text</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType kDial;
|
||||
kDial,
|
||||
/**
|
||||
* 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
|
||||
@@ -103,7 +102,7 @@ class BuiltInWidgets {
|
||||
* <td>How long, in seconds, should past data be visible for</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType kGraph;
|
||||
kGraph,
|
||||
/**
|
||||
* Displays a boolean value as a large colored box.
|
||||
* <br>Supported types:
|
||||
@@ -121,7 +120,7 @@ class BuiltInWidgets {
|
||||
* <td>Can be specified as a string or a number</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType kBooleanBox;
|
||||
kBooleanBox,
|
||||
/**
|
||||
* Displays a boolean with a large interactive toggle button.
|
||||
* <br>Supported types:
|
||||
@@ -130,7 +129,7 @@ class BuiltInWidgets {
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
static const WidgetType kToggleButton;
|
||||
kToggleButton,
|
||||
/**
|
||||
* Displays a boolean with a fixed-size toggle switch.
|
||||
* <br>Supported types:
|
||||
@@ -139,7 +138,7 @@ class BuiltInWidgets {
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
static const WidgetType kToggleSwitch;
|
||||
kToggleSwitch,
|
||||
/**
|
||||
* Displays an analog input or a raw number with a number bar.
|
||||
* <br>Supported types:
|
||||
@@ -162,7 +161,7 @@ class BuiltInWidgets {
|
||||
* bar</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType kVoltageView;
|
||||
kVoltageView,
|
||||
/**
|
||||
* Displays a {@link edu.wpi.first.wpilibj.PowerDistributionPanel
|
||||
* 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>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType kPowerDistributionPanel;
|
||||
kPowerDistributionPanel,
|
||||
/**
|
||||
* Displays a {@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser
|
||||
* SendableChooser} with a dropdown combo box with a list of options.
|
||||
@@ -185,7 +184,7 @@ class BuiltInWidgets {
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
static const WidgetType kComboBoxChooser;
|
||||
kComboBoxChooser,
|
||||
/**
|
||||
* Displays a {@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser
|
||||
* SendableChooser} with a toggle button for each available option.
|
||||
@@ -195,7 +194,7 @@ class BuiltInWidgets {
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
static const WidgetType kSplitButtonChooser;
|
||||
kSplitButtonChooser,
|
||||
/**
|
||||
* Displays an {@link edu.wpi.first.wpilibj.Encoder} displaying its speed,
|
||||
* total travelled distance, and its distance per tick. <br>Supported types:
|
||||
@@ -204,7 +203,7 @@ class BuiltInWidgets {
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
static const WidgetType kEncoder;
|
||||
kEncoder,
|
||||
/**
|
||||
* Displays a {@link edu.wpi.first.wpilibj.SpeedController SpeedController}.
|
||||
* 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>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType kSpeedController;
|
||||
kSpeedController,
|
||||
/**
|
||||
* Displays a command with a toggle button. Pressing the button will start the
|
||||
* command, and the button will automatically release when the command
|
||||
@@ -239,7 +238,7 @@ class BuiltInWidgets {
|
||||
* </ul>
|
||||
* <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
|
||||
* constants. Selecting the checkbox will start the command, and the checkbox
|
||||
@@ -249,7 +248,7 @@ class BuiltInWidgets {
|
||||
* </ul>
|
||||
* <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
|
||||
* toggle switch for enabling and disabling the controller. <br>Supported
|
||||
@@ -257,7 +256,7 @@ class BuiltInWidgets {
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
static const WidgetType kPIDController;
|
||||
kPIDController,
|
||||
/**
|
||||
* Displays an accelerometer with a number bar displaying the magnitude of the
|
||||
* 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>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType kAccelerometer;
|
||||
kAccelerometer,
|
||||
/**
|
||||
* Displays a 3-axis accelerometer with a number bar for each axis'
|
||||
* 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>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType k3AxisAccelerometer;
|
||||
k3AxisAccelerometer,
|
||||
/**
|
||||
* Displays a gyro with a dial from 0 to 360 degrees.
|
||||
* <br>Supported types:
|
||||
@@ -317,7 +316,7 @@ class BuiltInWidgets {
|
||||
* <tr><td>Show tick mark ring</td><td>Boolean</td><td>true</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType kGyro;
|
||||
kGyro,
|
||||
/**
|
||||
* Displays a relay with toggle buttons for each supported mode (off, on,
|
||||
* forward, reverse). <br>Supported types: <ul> <li>{@link
|
||||
@@ -325,7 +324,7 @@ class BuiltInWidgets {
|
||||
* </ul>
|
||||
* <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
|
||||
* 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>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType kDifferentialDrive;
|
||||
kDifferentialDrive,
|
||||
/**
|
||||
* 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
|
||||
@@ -357,7 +356,7 @@ class BuiltInWidgets {
|
||||
* <tr><td>Show velocity vectors</td><td>Boolean</td><td>true</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType kMecanumDrive;
|
||||
kMecanumDrive,
|
||||
/**
|
||||
* Displays a camera stream.
|
||||
* <br>Supported types:
|
||||
@@ -381,7 +380,7 @@ class BuiltInWidgets {
|
||||
* </td></tr>
|
||||
* </table>
|
||||
*/
|
||||
static const WidgetType kCameraStream;
|
||||
kCameraStream
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wpi/Twine.h>
|
||||
#include <wpi/StringRef.h>
|
||||
|
||||
namespace frc {
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace frc {
|
||||
*/
|
||||
class LayoutType {
|
||||
public:
|
||||
explicit LayoutType(const char* layoutName) : m_layoutName(layoutName) {}
|
||||
explicit constexpr LayoutType(const char* layoutName)
|
||||
: m_layoutName(layoutName) {}
|
||||
~LayoutType() = default;
|
||||
|
||||
/**
|
||||
@@ -30,7 +31,7 @@ class LayoutType {
|
||||
wpi::StringRef GetLayoutName() const;
|
||||
|
||||
private:
|
||||
wpi::StringRef m_layoutName;
|
||||
const char* m_layoutName;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "frc/ErrorBase.h"
|
||||
#include "frc/WPIErrors.h"
|
||||
#include "frc/shuffleboard/BuiltInLayouts.h"
|
||||
#include "frc/shuffleboard/LayoutType.h"
|
||||
#include "frc/shuffleboard/ShuffleboardComponentBase.h"
|
||||
#include "frc/shuffleboard/ShuffleboardValue.h"
|
||||
@@ -53,6 +54,16 @@ class ShuffleboardContainer : public virtual ShuffleboardValue,
|
||||
const std::vector<std::unique_ptr<ShuffleboardComponentBase>>& GetComponents()
|
||||
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
|
||||
* already exist at the time this method is called.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <wpi/Twine.h>
|
||||
|
||||
#include "frc/shuffleboard/BuiltInWidgets.h"
|
||||
#include "frc/shuffleboard/ShuffleboardComponent.h"
|
||||
#include "frc/shuffleboard/WidgetType.h"
|
||||
|
||||
@@ -16,6 +17,10 @@ namespace frc {
|
||||
|
||||
class ShuffleboardContainer;
|
||||
|
||||
namespace detail {
|
||||
const char* GetStringForWidgetType(BuiltInWidgets type);
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Abstract superclass for widgets.
|
||||
*
|
||||
@@ -24,12 +29,11 @@ class ShuffleboardContainer;
|
||||
* @tparam Derived the self type
|
||||
*/
|
||||
template <typename Derived>
|
||||
class ShuffleboardWidget
|
||||
: public ShuffleboardComponent<ShuffleboardWidget<Derived>> {
|
||||
class ShuffleboardWidget : public ShuffleboardComponent<Derived> {
|
||||
public:
|
||||
ShuffleboardWidget(ShuffleboardContainer& parent, const wpi::Twine& 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
|
||||
@@ -39,7 +43,18 @@ class ShuffleboardWidget
|
||||
* @return this widget object
|
||||
* @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());
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wpi/Twine.h>
|
||||
#include <wpi/StringRef.h>
|
||||
|
||||
namespace frc {
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace frc {
|
||||
*/
|
||||
class WidgetType {
|
||||
public:
|
||||
explicit WidgetType(const char* widgetName) : m_widgetName(widgetName) {}
|
||||
explicit constexpr WidgetType(const char* widgetName)
|
||||
: m_widgetName(widgetName) {}
|
||||
~WidgetType() = default;
|
||||
|
||||
/**
|
||||
@@ -30,7 +31,7 @@ class WidgetType {
|
||||
wpi::StringRef GetWidgetName() const;
|
||||
|
||||
private:
|
||||
wpi::StringRef m_widgetName;
|
||||
const char* m_widgetName;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
|
||||
@@ -83,11 +83,18 @@ TEST(WatchdogTest, SetTimeout) {
|
||||
|
||||
TEST(WatchdogTest, IsExpired) {
|
||||
Watchdog watchdog(0.2, [] {});
|
||||
EXPECT_FALSE(watchdog.IsExpired());
|
||||
watchdog.Enable();
|
||||
|
||||
EXPECT_FALSE(watchdog.IsExpired());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
EXPECT_TRUE(watchdog.IsExpired());
|
||||
|
||||
watchdog.Disable();
|
||||
EXPECT_TRUE(watchdog.IsExpired());
|
||||
|
||||
watchdog.Reset();
|
||||
EXPECT_FALSE(watchdog.IsExpired());
|
||||
}
|
||||
|
||||
TEST(WatchdogTest, Epochs) {
|
||||
@@ -118,7 +125,11 @@ TEST(WatchdogTest, Epochs) {
|
||||
EXPECT_EQ(0u, watchdogCounter) << "Watchdog triggered early";
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
TEST(WatchdogTest, DISABLED_MultiWatchdog) {
|
||||
#else
|
||||
TEST(WatchdogTest, MultiWatchdog) {
|
||||
#endif
|
||||
uint32_t watchdogCounter1 = 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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")
|
||||
public void testInit() {
|
||||
System.out.println("Default testInit() method... Overload me!");
|
||||
System.out.println("Default testInit() method... Override me!");
|
||||
}
|
||||
|
||||
/* ----------- Overridable periodic code ----------------- */
|
||||
@@ -139,7 +139,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
*/
|
||||
public void robotPeriodic() {
|
||||
if (m_rpFirstRun) {
|
||||
System.out.println("Default robotPeriodic() method... Overload me!");
|
||||
System.out.println("Default robotPeriodic() method... Override me!");
|
||||
m_rpFirstRun = false;
|
||||
}
|
||||
}
|
||||
@@ -151,7 +151,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
*/
|
||||
public void disabledPeriodic() {
|
||||
if (m_dpFirstRun) {
|
||||
System.out.println("Default disabledPeriodic() method... Overload me!");
|
||||
System.out.println("Default disabledPeriodic() method... Override me!");
|
||||
m_dpFirstRun = false;
|
||||
}
|
||||
}
|
||||
@@ -163,7 +163,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
*/
|
||||
public void autonomousPeriodic() {
|
||||
if (m_apFirstRun) {
|
||||
System.out.println("Default autonomousPeriodic() method... Overload me!");
|
||||
System.out.println("Default autonomousPeriodic() method... Override me!");
|
||||
m_apFirstRun = false;
|
||||
}
|
||||
}
|
||||
@@ -175,7 +175,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
*/
|
||||
public void teleopPeriodic() {
|
||||
if (m_tpFirstRun) {
|
||||
System.out.println("Default teleopPeriodic() method... Overload me!");
|
||||
System.out.println("Default teleopPeriodic() method... Override me!");
|
||||
m_tpFirstRun = false;
|
||||
}
|
||||
}
|
||||
@@ -188,7 +188,7 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
@SuppressWarnings("PMD.JUnit4TestShouldUseTestAnnotation")
|
||||
public void testPeriodic() {
|
||||
if (m_tmpFirstRun) {
|
||||
System.out.println("Default testPeriodic() method... Overload me!");
|
||||
System.out.println("Default testPeriodic() method... Override me!");
|
||||
m_tmpFirstRun = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,7 +667,7 @@ public class PIDBase extends SendableBase implements PIDInterface, PIDOutput {
|
||||
*
|
||||
* @param pidSource the type of input
|
||||
*/
|
||||
void setPIDSourceType(PIDSourceType pidSource) {
|
||||
public void setPIDSourceType(PIDSourceType pidSource) {
|
||||
m_pidInput.setPIDSourceType(pidSource);
|
||||
}
|
||||
|
||||
@@ -676,7 +676,7 @@ public class PIDBase extends SendableBase implements PIDInterface, PIDOutput {
|
||||
*
|
||||
* @return the PID controller input type
|
||||
*/
|
||||
PIDSourceType getPIDSourceType() {
|
||||
public PIDSourceType 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.
|
||||
*
|
||||
* <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
|
||||
private static final List<Ultrasonic> m_sensors = new ArrayList<>();
|
||||
// automatic round robin mode
|
||||
private static boolean m_automaticEnabled;
|
||||
private static volatile boolean m_automaticEnabled;
|
||||
private DigitalInput m_echoChannel;
|
||||
private DigitalOutput m_pingChannel;
|
||||
private boolean m_allocatedChannels;
|
||||
@@ -70,30 +70,18 @@ public class Ultrasonic extends SendableBase implements PIDSource {
|
||||
private static class UltrasonicChecker extends Thread {
|
||||
@Override
|
||||
public synchronized void run() {
|
||||
int sensorIndex = 0;
|
||||
Ultrasonic ultrasonic;
|
||||
while (m_automaticEnabled) {
|
||||
//lock list to ensure deletion doesn't occur between empty check and retrieving sensor
|
||||
synchronized (m_sensors) {
|
||||
if (m_sensors.isEmpty()) {
|
||||
return;
|
||||
for (Ultrasonic sensor: m_sensors) {
|
||||
if (!m_automaticEnabled) {
|
||||
break;
|
||||
}
|
||||
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() {
|
||||
m_queueMutex.lock();
|
||||
try {
|
||||
m_isExpired = false;
|
||||
|
||||
m_watchdogs.remove(this);
|
||||
m_schedulerWaiter.signalAll();
|
||||
} finally {
|
||||
|
||||
@@ -121,6 +121,7 @@ class WatchdogTest {
|
||||
void isExpiredTest() {
|
||||
final Watchdog watchdog = new Watchdog(0.2, () -> {
|
||||
});
|
||||
assertFalse(watchdog.isExpired());
|
||||
watchdog.enable();
|
||||
|
||||
assertFalse(watchdog.isExpired());
|
||||
@@ -130,6 +131,12 @@ class WatchdogTest {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
assertTrue(watchdog.isExpired());
|
||||
|
||||
watchdog.disable();
|
||||
assertTrue(watchdog.isExpired());
|
||||
|
||||
watchdog.reset();
|
||||
assertFalse(watchdog.isExpired());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -25,8 +25,27 @@ std::shared_ptr<Pipe> Pipe::Create(Loop& loop, bool ipc) {
|
||||
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() {
|
||||
auto client = Create(GetLoopRef());
|
||||
auto client = Create(GetLoopRef(), GetRaw()->ipc);
|
||||
if (!client) return nullptr;
|
||||
if (!Accept(client)) {
|
||||
client->Release();
|
||||
|
||||
@@ -56,6 +56,18 @@ class Pipe final : public NetworkStreamImpl<Pipe, uv_pipe_t> {
|
||||
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.
|
||||
*
|
||||
@@ -176,6 +188,12 @@ class Pipe final : public NetworkStreamImpl<Pipe, uv_pipe_t> {
|
||||
|
||||
private:
|
||||
Pipe* DoAccept() override;
|
||||
|
||||
struct ReuseData {
|
||||
std::function<void()> callback;
|
||||
bool ipc;
|
||||
};
|
||||
std::unique_ptr<ReuseData> m_reuseData;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user