Compare commits

...

28 Commits

Author SHA1 Message Date
Peter Johnson
60c2f59051 C++ CameraServer: initialize default Usb camera device number (#1601)
Otherwise just plain StartAutomaticCapture() starts with random number.
2019-02-14 22:39:40 -08:00
Peter Johnson
d55ca191b8 CameraServer: Add switched camera support (#1600)
This adds a new function "addSwitchedCamera" that creates and publishes a
virtual camera where the published stream information is consistent even if
the mjpeg server source is switched to a different camera.

Previously, changing the source of the mjpeg server resulted in updating the
stream information published for that source.
2019-02-14 22:05:40 -08:00
Sam Carlberg
e8b24717c7 C++ Shuffleboard fixes (#1595)
* Fix C++ ShuffleboardComponent template type

* Fix `WithWidget(WidgetType&)`not being properly capitalized

* Fix data members across dll boundaries by using enum for built in types
2019-02-14 20:44:30 -08:00
Tyler Veness
182758c05b Fix Ultrasonic sensor runner thread (#1598)
When there is more than one ultrasonic sensor, only the last sensor
instantiated would work due to incorrect array index management. This
replaces the previous approach with range-based for loops like the C++
implementation.

Supersedes #1589.
2019-02-14 20:25:56 -08:00
Peter Johnson
74f7ba04b0 Java PIDBase: Make setPIDSourceType and getPIDSourceType public (#1599)
Fixes #1596.
2019-02-13 23:41:05 -08:00
Thad House
997d4fdf47 Update HAL GetStackTrace to properly report user errors (#1594)
With the move of the HAL, the old value no longer worked, as the JNI call is in a different namespace
2019-02-11 20:17:31 -08:00
Thad House
76d9e26633 uv: Add reuse to pipe (#1577)
Needed for a reconnection API
2019-02-09 00:09:22 -08:00
Thad House
a230c814cb Add support for WPILib vscode extension for allwpilib C++ intellisense (#1590) 2019-02-09 00:07:48 -08:00
Thad House
12cb77cd7c Fix DS Comm button tests (#1591)
Mac requires the double braces
2019-02-09 00:07:24 -08:00
Thad House
8a9822a96b Allow multiple instances of the same PDP (#1582)
Previously multiple instances would overrun with each other. This make all instances get the same HAL handle.
2019-02-06 22:51:34 -08:00
Thad House
a9371a7586 Fix missing exposure property on windows USB cameras (#1571) 2019-02-06 22:49:55 -08:00
Peter Johnson
6992f5421f cscore: Avoid crash if invalid (null) source set in MjpegServer (#1585)
Fixes robotpy/robotpy-cscore#60
2019-02-03 00:22:07 -08:00
Tyler Veness
43696956d2 Fix Watchdog incorrectly resetting expiration flag upon disable (#1580)
Resetting the flag should only occur in Enable() and Reset().
IterativeRobotBase needs the flag to remain set to print epochs after
disabling the Watchdog.
2019-02-02 00:22:07 -08:00
Thad House
ae3fd5adac Fix docs search having unspecified module directory (#1568)
Also add Doxygen comment for C++ filesystem API.
2019-02-02 00:20:57 -08:00
Jaci Brunning
404666b298 Fix Halsim DS Button Format (#1583) 2019-02-01 23:23:04 -08:00
sciencewhiz
1eb4c99d15 Update README for 2019 changes (#1569)
Fixes #1562
2019-02-01 23:05:41 -08:00
Thad House
910b9f3af7 Add support for camera descriptions on windows (#1572)
For now, has to force enumerate all cameras to find the matching one
2019-02-01 23:04:12 -08:00
Thad House
09d90b02fb Remove prints and unecessary items from windows GetNetworkInterfaces (#1573) 2019-02-01 23:02:12 -08:00
Peter Johnson
0e1f9c2ed2 ntcore: Read ini escaped quotes correctly (#1579) 2019-02-01 23:01:00 -08:00
Thad House
f156a00117 wpiutil uv: Pass IPC value of listening pipe to accepted pipe (#1576)
Otherwise, the acception fails.
2019-01-30 20:37:28 -08:00
Thad House
4a6087ed56 Disable watchdog test on mac (#1578)
It can't be held property on the asure test system
2019-01-30 20:35:16 -08:00
Peter Johnson
88a09dd13a cscore: Handle USB camera integer menus (#1561)
The Pi Camera is one of these.  Previously, integers were just being
cast to a string instead of formatted as a string.
2019-01-24 22:46:29 -08:00
Christopher Cantrell
7d19596367 Changed terminology from "Overload" to "Override" (#1563) 2019-01-24 22:45:05 -08:00
Jaci Brunning
bd05dfa1c7 Fix ds_socket matchinfo (#1555) 2019-01-16 21:32:34 -08:00
Peter Johnson
05d6660a6b cscore: Add config json to VideoSink (#1543)
Same format as VideoSource.

Refactor properties json handling into PropertyContainer.
2019-01-11 20:33:05 -08:00
Peter Johnson
1349dd4bd8 Make MjpegServer functions public (#1545) 2019-01-11 17:53:44 -08:00
Peter Johnson
fdf298b172 CameraServer: Return MjpegServer from StartAutomaticCapture(VideoSource) (#1544) 2019-01-11 16:44:49 -08:00
Peter Johnson
453a9047e4 Fix cmake hal sim build (#1539)
A source directory was named incorrectly in CamelCase.
2019-01-11 02:38:35 -08:00
61 changed files with 952 additions and 347 deletions

View File

@@ -0,0 +1,6 @@
{
"enableCppIntellisense": true,
"currentLanguage": "cpp",
"projectYear": "2019",
"teamNumber": 0
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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() {

View File

@@ -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.

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
}
};

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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, &paramFlag))) {
if (SUCCEEDED(pProcAmp->Get(tagVideoProc, &newValue, &paramFlag))) {
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, &paramVal,
&paramFlag); // 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, &paramFlag))) {
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;
}
}
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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")

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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)),

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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; }

View File

@@ -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();
}

View File

@@ -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"};

View File

@@ -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"};

View File

@@ -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());

View File

@@ -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)];
}

View File

@@ -12,6 +12,7 @@
#include "frc/RobotBase.h"
namespace frc {
/** WPILib FileSystem namespace */
namespace filesystem {
/**

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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());
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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";
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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.

View File

@@ -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
}
}
}
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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();

View File

@@ -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;
};
/**