Compare commits

...

42 Commits

Author SHA1 Message Date
Peter Johnson
e97e7a7611 Add DriverStationSim.notifyNewData (#1537) 2019-01-09 23:47:42 -08:00
Thad House
308bdbe298 cscore: Windows UsbCamera: Use custom windows message for initial connection (#1534)
WM_CREATE is being called at a time where the message pump cannot be called back into.
2019-01-09 22:52:24 -08:00
Peter Johnson
f889b45d59 cscore: MjpegServer: Fix FPS limiting (#1536)
This uses a 1-second window average to try to hit the desired FPS.
2019-01-09 22:50:34 -08:00
Peter Johnson
444b899a9f Java: Fix Timer.get() handling of accumulated time (#1531)
Fixes #1530.
2019-01-08 19:43:49 -08:00
Tyler Veness
f121ccff0d Avoid Watchdog thread clobbering m_isExpired flag after callback (#1527) 2019-01-08 19:37:59 -08:00
Peter Johnson
bc2c932f92 Fix MotorSafety deadlock (#1526)
Some instances of StopMotor (most notably DifferentialDrive) call Feed(),
which deadlocks due to Check() holding the same lock.

Fixes #1525.
2019-01-02 20:58:39 -08:00
Thad House
6bdd7ce506 Update docs for disabled init to match all the other modes (#1523)
Closes #1522
2018-12-31 19:11:35 -08:00
Thad House
c12d7729e3 Update examples to use VictorSPX's rather then Sparks (#1521) 2018-12-31 13:45:09 -08:00
Dustin Spicuzza
3635116049 cscore: set charset of displayed pages (#1520)
All of our strings are UTF-8, but the default charset for HTML<5 was ISO-8859-1
2018-12-30 23:49:24 -08:00
Peter Johnson
6105873cbe Provide alternate device paths info for USB cameras (#1519)
This is primarily for use on Linux to get by-id or by-path device names.

This information is now part of UsbCameraInfo.

A new entry point was added to UsbCamera to get that camera's UsbCameraInfo.

The alternate paths are also returned in EnumerateUsbCameras.
2018-12-30 11:48:18 -08:00
Sam Carlberg
80f87ff8ad Allow video sources to be added to Shuffleboard (#1453)
Add a Sendable wrapper for VideoSource objects.

Add convenience methods for adding video sources directly to containers
so users won't have to manually wrap video sources.
2018-12-30 11:45:41 -08:00
Peter Johnson
a2368a6199 Watchdog: add timeout message suppression feature
Was part of reverted #1486
2018-12-30 00:16:50 -08:00
Peter Johnson
ae3cb6c83b Watchdog.java: add comment fixes from reverted #1442 2018-12-30 00:16:50 -08:00
Peter Johnson
f0f196e5b3 Revert "MotorSafety: Use Watchdog instead of DS class polling (#1442)"
This reverts commit 26e8e587f9.
2018-12-30 00:16:50 -08:00
Peter Johnson
7c35355d29 Revert "Suppress Watchdog's generic timeout message in MotorSafety (#1486)"
This reverts commit 41596608cc.
2018-12-30 00:16:50 -08:00
Peter Johnson
75cc09a9e4 Revert "Suppress timeout message in C++ MotorSafety class (#1512)"
This reverts commit 300eeb330d.
2018-12-30 00:16:50 -08:00
Peter Johnson
0e2e180635 PowerDistributionPanelTest.cpp: Include thread 2018-12-30 00:16:50 -08:00
Sam Carlberg
01d1322066 Add constants for built-in Shuffleboard widgets and layouts (#1468)
Prevents users from having to remember (and correctly type) the names of Shuffleboard widgets.
2018-12-29 17:22:47 -08:00
Peter Johnson
ceed1d74dc ntcore: Strip whitespace from IP addresses (#1516) 2018-12-29 14:47:25 -08:00
Peter Johnson
e1bf623997 cscore: handle HTTP stream hangs (#1513)
Automatically disconnect the HTTP stream if no frames have been received
for 1 second.
2018-12-29 14:08:45 -08:00
Thad House
d46ce13ffe Fix CAN API timing out incorrectly (#1497)
HAL_GetFPGATime returns 0 if it starts with a non zero status.

Always use monotonic clock for CAN times, rather then trying to sync FPGA.

Change timeout from 50 ms to 100 ms.
2018-12-29 13:57:23 -08:00
Tyler Veness
300eeb330d Suppress timeout message in C++ MotorSafety class (#1512)
wpilibj's default constructor is already correct.
2018-12-27 14:01:02 -06:00
Andrew Dassonville
d817001259 Only write version information on real robot (#1510)
Writing to the versions file throws an exception on Windows, and might
write weird files on Linux.
2018-12-27 00:59:49 -06:00
Austin Shalit
8ac4b113a5 Fix build on macOS 10.14.2 (#1509) 2018-12-25 22:58:04 -06:00
Tyler Veness
f3864e9abb Move deprecated message in C++ from class level to constructor (#1505)
This prevents deprecation messages when including a header but not
instantiating the class. SampleRobot was addressed by #1503.
2018-12-25 01:14:24 -06:00
Thad House
799c3ea8a6 Disable gyro sim test on mac (#1506)
It fails with a mutex error very often, and we have not been able to figure out the issue.

It breaks CI.
2018-12-25 01:13:59 -06:00
David Vo
8d95c38e39 Watchdog: Fix potential IllegalFormatException (#1508)
If an epoch name contains a % symbol, this could've lead to an exception
at runtime where the number of arguments mismatches the format string.
2018-12-25 01:12:44 -06:00
Thad House
a7f4e29b73 Update versioning and vscode plugin (#1498)
versioning fixes multi tag issue
2018-12-24 15:46:41 -06:00
Thad House
b88369f5e8 Move deprecation warning for SampleRobot to constructor (#1503)
This way, the warning doesn't occur when including wpilib.h

Closes #1501
2018-12-24 15:46:12 -06:00
Thad House
ce6f1d0588 Change deperecation year for serial port direct constructor (#1504) 2018-12-24 15:17:19 -06:00
Thad House
f163216a4c Add support for raspbian extraction (#1494)
This is how I did it in C#, and it was the most reliable way I could find.
2018-12-24 15:15:15 -06:00
Thad House
c449ef1064 Unconditionally await in awaitUntil (#1499)
Negative numbers are properly handled, which will reduce chances of deadlocks.
2018-12-24 15:07:47 -06:00
Thad House
6593f4346e Default to DS attached to true in sim (#1500)
Otherwise, it is required to be set manually, which isn't obvious.

This is because the HighLevel DS classes check that the ds is attached before enabling
2018-12-24 02:06:53 -06:00
Thad House
ce1367a115 Make JNI library not found message clearer (#1495)
Closes #1472
2018-12-21 00:26:53 -08:00
Thad House
0d7d880261 Renable full Java 8 Compatibility (#1493) 2018-12-21 00:25:23 -08:00
Thad House
ca2acec88c Update to NI Libraries 2019v12 (#1492) 2018-12-21 00:24:44 -08:00
Thad House
3721463eb3 Don't use symbolic path searching for USB detection (#1491)
The USB directories we care about are not symbolic links, so we can ignore them.

There was a recursive symbolic link, which was causing failure.
2018-12-19 14:40:01 -08:00
Thad House
6e8f8be370 Make C++ examples able to run GradleRIO unit tests (#1490)
Closes #1484
2018-12-17 09:26:20 -08:00
Thad House
d84240d8e9 Bump opencv version to 3.4.4-4 (#1489) 2018-12-14 20:17:51 -08:00
Thad House
1823cb2b68 Fix periodic can packets not being received properly (#1488)
Constant calls would result in timeout, because a valid packet wasn't being returned early.

Also fix data copies (was copying in wrong direction).
2018-12-14 17:43:48 -08:00
Tyler Veness
41596608cc Suppress Watchdog's generic timeout message in MotorSafety (#1486) 2018-12-14 10:53:33 -08:00
Thad House
0c3b488e18 Make ntcore instances not smart pointers (#1485)
During static shutdown, the instances hang on windows 7, so let them leak instead.
2018-12-13 21:33:58 -08:00
132 changed files with 2367 additions and 409 deletions

View File

@@ -1,9 +1,9 @@
plugins {
id 'base'
id 'edu.wpi.first.wpilib.versioning.WPILibVersioningPlugin' version '2.2'
id 'edu.wpi.first.wpilib.versioning.WPILibVersioningPlugin' version '2.3'
id 'edu.wpi.first.NativeUtils' version '2.1.2'
id 'edu.wpi.first.GradleJni' version '0.3.1'
id 'edu.wpi.first.GradleVsCode' version '0.6.1'
id 'edu.wpi.first.GradleVsCode' version '0.7.1'
id 'idea'
id 'visual-studio'
id 'com.gradle.build-scan' version '2.0.2'

View File

@@ -123,7 +123,7 @@ public final class CameraServer {
}
}
return values.toArray(String[]::new);
return values.toArray(new String[0]);
}
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP"})

View File

@@ -17,6 +17,12 @@ int main() {
for (const auto& caminfo : cs::EnumerateUsbCameras(&status)) {
wpi::outs() << caminfo.dev << ": " << caminfo.path << " (" << caminfo.name
<< ")\n";
if (!caminfo.otherPaths.empty()) {
wpi::outs() << "Other device paths:\n";
for (auto&& path : caminfo.otherPaths)
wpi::outs() << " " << path << '\n';
}
cs::UsbCamera camera{"usbcam", caminfo.dev};
wpi::outs() << "Properties:\n";

View File

@@ -112,6 +112,7 @@ public class CameraServerJNI {
// UsbCamera Source Functions
//
public static native String getUsbCameraPath(int source);
public static native UsbCameraInfo getUsbCameraInfo(int source);
//
// HttpCamera Source Functions

View File

@@ -47,6 +47,13 @@ public class UsbCamera extends VideoCamera {
return CameraServerJNI.getUsbCameraPath(m_handle);
}
/**
* Get the full camera information for the device.
*/
public UsbCameraInfo getInfo() {
return CameraServerJNI.getUsbCameraInfo(m_handle);
}
/**
* Set how verbose the camera connection messages are.
*

View File

@@ -17,11 +17,14 @@ public class UsbCameraInfo {
* @param dev Device number (e.g. N in '/dev/videoN' on Linux)
* @param path Path to device if available (e.g. '/dev/video0' on Linux)
* @param name Vendor/model name of the camera as provided by the USB driver
* @param otherPaths Other path aliases to device
*/
public UsbCameraInfo(int dev, String path, String name) {
@SuppressWarnings("PMD.ArrayIsStoredDirectly")
public UsbCameraInfo(int dev, String path, String name, String[] otherPaths) {
this.dev = dev;
this.path = path;
this.name = name;
this.otherPaths = otherPaths;
}
/**
@@ -41,4 +44,10 @@ public class UsbCameraInfo {
*/
@SuppressWarnings("MemberName")
public String name;
/**
* Other path aliases to device (e.g. '/dev/v4l/by-id/...' etc on Linux).
*/
@SuppressWarnings("MemberName")
public String[] otherPaths;
}

View File

@@ -30,6 +30,12 @@ HttpCameraImpl::HttpCameraImpl(const wpi::Twine& name, CS_HttpCameraKind kind,
HttpCameraImpl::~HttpCameraImpl() {
m_active = false;
// force wakeup of monitor thread
m_monitorCond.notify_one();
// join monitor thread
if (m_monitorThread.joinable()) m_monitorThread.join();
// Close file if it's open
{
std::lock_guard<wpi::mutex> lock(m_mutex);
@@ -54,6 +60,31 @@ void HttpCameraImpl::Start() {
// Kick off the stream and settings threads
m_streamThread = std::thread(&HttpCameraImpl::StreamThreadMain, this);
m_settingsThread = std::thread(&HttpCameraImpl::SettingsThreadMain, this);
m_monitorThread = std::thread(&HttpCameraImpl::MonitorThreadMain, this);
}
void HttpCameraImpl::MonitorThreadMain() {
while (m_active) {
std::unique_lock<wpi::mutex> lock(m_mutex);
// sleep for 1 second between checks
m_monitorCond.wait_for(lock, std::chrono::seconds(1),
[=] { return !m_active; });
if (!m_active) break;
// check to see if we got any frames, and close the stream if not
// (this will result in an error at the read point, and ultimately
// a reconnect attempt)
if (m_streamConn && m_frameCount == 0) {
SWARNING("Monitor detected stream hung, disconnecting");
m_streamConn->stream->close();
}
// reset the frame counter
m_frameCount = 0;
}
SDEBUG("Monitor Thread exiting");
}
void HttpCameraImpl::StreamThreadMain() {
@@ -86,6 +117,10 @@ void HttpCameraImpl::StreamThreadMain() {
// stream
DeviceStream(conn->is, boundary);
{
std::unique_lock<wpi::mutex> lock(m_mutex);
m_streamConn = nullptr;
}
}
SDEBUG("Camera Thread exiting");
@@ -120,6 +155,7 @@ wpi::HttpConnection* HttpCameraImpl::DeviceStreamConnect(
// update m_streamConn
{
std::lock_guard<wpi::mutex> lock(m_mutex);
m_frameCount = 1; // avoid a race with monitor thread
m_streamConn = std::move(connPtr);
}
@@ -229,6 +265,7 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::raw_istream& is,
}
PutFrame(VideoMode::PixelFormat::kMJPEG, width, height, imageBuf,
wpi::Now());
++m_frameCount;
return true;
}
@@ -246,6 +283,7 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::raw_istream& is,
image->width = width;
image->height = height;
PutFrame(std::move(image), wpi::Now());
++m_frameCount;
return true;
}

View File

@@ -111,10 +111,14 @@ class HttpCameraImpl : public SourceImpl {
void SettingsThreadMain();
void DeviceSendSettings(wpi::HttpRequest& req);
// The monitor thread
void MonitorThreadMain();
std::atomic_bool m_connected{false};
std::atomic_bool m_active{true}; // set to false to terminate thread
std::thread m_streamThread;
std::thread m_settingsThread;
std::thread m_monitorThread;
//
// Variables protected by m_mutex
@@ -130,6 +134,8 @@ class HttpCameraImpl : public SourceImpl {
size_t m_nextLocation{0};
int m_prefLocation{-1}; // preferred location
std::atomic_int m_frameCount{0};
wpi::condition_variable m_sinkEnabledCond;
wpi::StringMap<wpi::SmallString<16>> m_settings;
@@ -137,6 +143,8 @@ class HttpCameraImpl : public SourceImpl {
wpi::StringMap<wpi::SmallString<16>> m_streamSettings;
std::atomic_bool m_streamSettingsUpdated{false};
wpi::condition_variable m_monitorCond;
};
class AxisCameraImpl : public HttpCameraImpl {

View File

@@ -335,7 +335,8 @@ bool MjpegServerImpl::ConnThread::ProcessCommand(wpi::raw_ostream& os,
void MjpegServerImpl::ConnThread::SendHTMLHeadTitle(
wpi::raw_ostream& os) const {
os << "<html><head><title>" << m_name << " CameraServer</title>";
os << "<html><head><title>" << m_name << " CameraServer</title>"
<< "<meta charset=\"UTF-8\">";
}
// Send the root html file with controls for all the settable properties.
@@ -413,6 +414,15 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
}
}
status = 0;
auto info = GetUsbCameraInfo(Instance::GetInstance().FindSource(source).first,
&status);
if (status == CS_OK) {
os << "<p>USB device path: " << info.path << '\n';
for (auto&& path : info.otherPaths)
os << "<p>Alternate device path: " << path << '\n';
}
os << "<p>Supported Video Modes:</p>\n";
os << "<table cols=\"4\" style=\"border: 1px solid black\">\n";
os << "<tr><th>Pixel Format</th>"
@@ -633,8 +643,9 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) {
Frame::Time lastFrameTime = 0;
Frame::Time timePerFrame = 0;
if (m_fps != 0) timePerFrame = 1000000.0 / m_fps;
// Allow fudge factor of 1 ms in frame rate
if (timePerFrame >= 1000) timePerFrame -= 1000;
Frame::Time averageFrameTime = 0;
Frame::Time averagePeriod = 1000000; // 1 second window
if (averagePeriod < timePerFrame) averagePeriod = timePerFrame * 10;
StartStream();
while (m_active && !os.has_error()) {
@@ -655,10 +666,26 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) {
continue;
}
if (frame.GetTime() < (lastFrameTime + timePerFrame)) {
// Limit FPS; sleep for 10 ms so we don't consume all processor time
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
auto thisFrameTime = frame.GetTime();
if (thisFrameTime != 0 && timePerFrame != 0 && lastFrameTime != 0) {
Frame::Time deltaTime = thisFrameTime - lastFrameTime;
// drop frame if it is early compared to the desired frame rate AND
// the current average is higher than the desired average
if (deltaTime < timePerFrame && averageFrameTime < timePerFrame) {
// sleep for 1 ms so we don't consume all processor time
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
}
// update average
if (averageFrameTime != 0) {
averageFrameTime =
averageFrameTime * (averagePeriod - timePerFrame) / averagePeriod +
deltaTime * timePerFrame / averagePeriod;
} else {
averageFrameTime = deltaTime;
}
}
int width = m_width != 0 ? m_width : frame.GetOriginalWidth();
@@ -695,7 +722,7 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) {
// print the individual mimetype and the length
// sending the content-length fixes random stream disruption observed
// with firefox
lastFrameTime = frame.GetTime();
lastFrameTime = thisFrameTime;
double timestamp = lastFrameTime / 1000000.0;
header.clear();
oss << "\r\n--" BOUNDARY "\r\n"

View File

@@ -12,6 +12,25 @@
using namespace cs;
static void ConvertToC(CS_UsbCameraInfo* out, const UsbCameraInfo& in) {
out->dev = in.dev;
out->path = ConvertToC(in.path);
out->name = ConvertToC(in.name);
out->otherPaths = static_cast<char**>(
wpi::CheckedMalloc(in.otherPaths.size() * sizeof(char*)));
out->otherPathsCount = in.otherPaths.size();
for (size_t i = 0; i < in.otherPaths.size(); ++i)
out->otherPaths[i] = cs::ConvertToC(in.otherPaths[i]);
}
static void FreeUsbCameraInfo(CS_UsbCameraInfo* info) {
std::free(info->path);
std::free(info->name);
for (int i = 0; i < info->otherPathsCount; ++i)
std::free(info->otherPaths[i]);
std::free(info->otherPaths);
}
extern "C" {
CS_Source CS_CreateUsbCameraDev(const char* name, int dev, CS_Status* status) {
@@ -27,26 +46,34 @@ char* CS_GetUsbCameraPath(CS_Source source, CS_Status* status) {
return ConvertToC(cs::GetUsbCameraPath(source, status));
}
CS_UsbCameraInfo* CS_GetUsbCameraInfo(CS_Source source, CS_Status* status) {
auto info = cs::GetUsbCameraInfo(source, status);
if (*status != CS_OK) return nullptr;
CS_UsbCameraInfo* out = static_cast<CS_UsbCameraInfo*>(
wpi::CheckedMalloc(sizeof(CS_UsbCameraInfo)));
ConvertToC(out, info);
return out;
}
CS_UsbCameraInfo* CS_EnumerateUsbCameras(int* count, CS_Status* status) {
auto cameras = cs::EnumerateUsbCameras(status);
CS_UsbCameraInfo* out = static_cast<CS_UsbCameraInfo*>(
wpi::CheckedMalloc(cameras.size() * sizeof(CS_UsbCameraInfo)));
*count = cameras.size();
for (size_t i = 0; i < cameras.size(); ++i) {
out[i].dev = cameras[i].dev;
out[i].path = ConvertToC(cameras[i].path);
out[i].name = ConvertToC(cameras[i].name);
}
for (size_t i = 0; i < cameras.size(); ++i) ConvertToC(&out[i], cameras[i]);
return out;
}
void CS_FreeEnumeratedUsbCameras(CS_UsbCameraInfo* cameras, int count) {
if (!cameras) return;
for (int i = 0; i < count; ++i) {
std::free(cameras[i].path);
std::free(cameras[i].name);
}
for (int i = 0; i < count; ++i) FreeUsbCameraInfo(&cameras[i]);
std::free(cameras);
}
void CS_FreeUsbCameraInfo(CS_UsbCameraInfo* info) {
if (!info) return;
FreeUsbCameraInfo(info);
std::free(info);
}
} // extern "C"

View File

@@ -185,11 +185,14 @@ static inline bool CheckStatus(JNIEnv* env, CS_Status status) {
static jobject MakeJObject(JNIEnv* env, const cs::UsbCameraInfo& info) {
static jmethodID constructor = env->GetMethodID(
usbCameraInfoCls, "<init>", "(ILjava/lang/String;Ljava/lang/String;)V");
usbCameraInfoCls, "<init>",
"(ILjava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V");
JLocal<jstring> path(env, MakeJString(env, info.path));
JLocal<jstring> name(env, MakeJString(env, info.name));
JLocal<jobjectArray> otherPaths(env, MakeJStringArray(env, info.otherPaths));
return env->NewObject(usbCameraInfoCls, constructor,
static_cast<jint>(info.dev), path.obj(), name.obj());
static_cast<jint>(info.dev), path.obj(), name.obj(),
otherPaths.obj());
}
static jobject MakeJObject(JNIEnv* env, const cs::VideoMode& videoMode) {
@@ -975,6 +978,21 @@ Java_edu_wpi_cscore_CameraServerJNI_getUsbCameraPath
return MakeJString(env, str);
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: getUsbCameraInfo
* Signature: (I)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL
Java_edu_wpi_cscore_CameraServerJNI_getUsbCameraInfo
(JNIEnv* env, jclass, jint source)
{
CS_Status status = 0;
auto info = cs::GetUsbCameraInfo(source, &status);
if (!CheckStatus(env, status)) return nullptr;
return MakeJObject(env, info);
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: getHttpCameraKind

View File

@@ -223,6 +223,17 @@ struct CS_Event {
const char* valueStr;
};
/**
* USB camera infomation
*/
typedef struct CS_UsbCameraInfo {
int dev;
char* path;
char* name;
int otherPathsCount;
char** otherPaths;
} CS_UsbCameraInfo;
/**
* @defgroup cscore_property_cfunc Property Functions
* @{
@@ -322,6 +333,7 @@ void CS_SetCameraExposureManual(CS_Source source, int value, CS_Status* status);
* @{
*/
char* CS_GetUsbCameraPath(CS_Source source, CS_Status* status);
CS_UsbCameraInfo* CS_GetUsbCameraInfo(CS_Source source, CS_Status* status);
/** @} */
/**
@@ -456,15 +468,6 @@ void CS_Shutdown(void);
* @{
*/
/**
* USB camera infomation
*/
typedef struct CS_UsbCameraInfo {
int dev;
char* path;
char* name;
} CS_UsbCameraInfo;
CS_UsbCameraInfo* CS_EnumerateUsbCameras(int* count, CS_Status* status);
void CS_FreeEnumeratedUsbCameras(CS_UsbCameraInfo* cameras, int count);
@@ -476,6 +479,7 @@ void CS_ReleaseEnumeratedSinks(CS_Sink* sinks, int count);
void CS_FreeString(char* str);
void CS_FreeEnumPropertyChoices(char** choices, int count);
void CS_FreeUsbCameraInfo(CS_UsbCameraInfo* info);
void CS_FreeHttpCameraUrls(char** urls, int count);
void CS_FreeEnumeratedProperties(CS_Property* properties, int count);

View File

@@ -47,11 +47,13 @@ namespace cs {
*/
struct UsbCameraInfo {
/** Device number (e.g. N in '/dev/videoN' on Linux) */
int dev;
int dev = -1;
/** Path to device if available (e.g. '/dev/video0' on Linux) */
std::string path;
/** Vendor/model name of the camera as provided by the USB driver */
std::string name;
/** Other path aliases to device (e.g. '/dev/v4l/by-id/...' etc on Linux) */
std::vector<std::string> otherPaths;
};
/**
@@ -267,6 +269,7 @@ void SetCameraExposureManual(CS_Source source, int value, CS_Status* status);
* @{
*/
std::string GetUsbCameraPath(CS_Source source, CS_Status* status);
UsbCameraInfo GetUsbCameraInfo(CS_Source source, CS_Status* status);
/** @} */
/**

View File

@@ -449,6 +449,11 @@ class UsbCamera : public VideoCamera {
*/
std::string GetPath() const;
/**
* Get the full camera information for the device.
*/
UsbCameraInfo GetInfo() const;
/**
* Set how verbose the camera connection messages are.
*

View File

@@ -260,6 +260,11 @@ inline std::string UsbCamera::GetPath() const {
return ::cs::GetUsbCameraPath(m_handle, &m_status);
}
inline UsbCameraInfo UsbCamera::GetInfo() const {
m_status = 0;
return ::cs::GetUsbCameraInfo(m_handle, &m_status);
}
inline void UsbCamera::SetConnectVerbose(int level) {
m_status = 0;
SetProperty(GetSourceProperty(m_handle, "connect_verbose", &m_status), level,

View File

@@ -24,6 +24,8 @@
#include <algorithm>
#include <wpi/FileSystem.h>
#include <wpi/Path.h>
#include <wpi/SmallString.h>
#include <wpi/memory.h>
#include <wpi/raw_ostream.h>
@@ -1278,17 +1280,79 @@ std::string GetUsbCameraPath(CS_Source source, CS_Status* status) {
return static_cast<UsbCameraImpl&>(*data->source).GetPath();
}
static const char* symlinkDirs[] = {"/dev/v4l/by-id", "/dev/v4l/by-path"};
UsbCameraInfo GetUsbCameraInfo(CS_Source source, CS_Status* status) {
UsbCameraInfo info;
auto data = Instance::GetInstance().GetSource(source);
if (!data || data->kind != CS_SOURCE_USB) {
*status = CS_INVALID_HANDLE;
return info;
}
std::string keypath = static_cast<UsbCameraImpl&>(*data->source).GetPath();
info.path = keypath;
// it might be a symlink; if so, find the symlink target (e.g. /dev/videoN),
// add that to the list and make it the keypath
if (wpi::sys::fs::is_symlink_file(keypath)) {
char* target = ::realpath(keypath.c_str(), nullptr);
if (target) {
keypath.assign(target);
info.otherPaths.emplace_back(keypath);
std::free(target);
}
}
// device number
wpi::StringRef fname = wpi::sys::path::filename(keypath);
if (fname.startswith("video")) fname.substr(5).getAsInteger(10, info.dev);
// description
info.name = GetDescriptionImpl(keypath.c_str());
// look through /dev/v4l/by-id and /dev/v4l/by-path for symlinks to the
// keypath
wpi::SmallString<128> path;
for (auto symlinkDir : symlinkDirs) {
if (DIR* dp = ::opendir(symlinkDir)) {
while (struct dirent* ep = ::readdir(dp)) {
if (ep->d_type == DT_LNK) {
path = symlinkDir;
path += '/';
path += ep->d_name;
char* target = ::realpath(path.c_str(), nullptr);
if (target) {
if (keypath == target) info.otherPaths.emplace_back(path.str());
std::free(target);
}
}
}
::closedir(dp);
}
}
// eliminate any duplicates
std::sort(info.otherPaths.begin(), info.otherPaths.end());
info.otherPaths.erase(
std::unique(info.otherPaths.begin(), info.otherPaths.end()),
info.otherPaths.end());
return info;
}
std::vector<UsbCameraInfo> EnumerateUsbCameras(CS_Status* status) {
std::vector<UsbCameraInfo> retval;
if (DIR* dp = opendir("/dev")) {
while (struct dirent* ep = readdir(dp)) {
if (DIR* dp = ::opendir("/dev")) {
while (struct dirent* ep = ::readdir(dp)) {
wpi::StringRef fname{ep->d_name};
if (!fname.startswith("video")) continue;
unsigned int dev = 0;
if (fname.substr(5).getAsInteger(10, dev)) continue;
UsbCameraInfo info;
info.dev = -1;
fname.substr(5).getAsInteger(10, info.dev);
info.dev = dev;
wpi::SmallString<32> path{"/dev/"};
path += fname;
info.path = path.str();
@@ -1296,20 +1360,47 @@ std::vector<UsbCameraInfo> EnumerateUsbCameras(CS_Status* status) {
info.name = GetDescriptionImpl(path.c_str());
if (info.name.empty()) continue;
retval.emplace_back(std::move(info));
if (dev >= retval.size()) retval.resize(info.dev + 1);
retval[info.dev] = std::move(info);
}
closedir(dp);
::closedir(dp);
} else {
// *status = ;
WPI_ERROR(Instance::GetInstance().logger, "Could not open /dev");
return retval;
}
// sort by device number
std::sort(retval.begin(), retval.end(),
[](const UsbCameraInfo& a, const UsbCameraInfo& b) {
return a.dev < b.dev;
});
// look through /dev/v4l/by-id and /dev/v4l/by-path for symlinks to
// /dev/videoN
wpi::SmallString<128> path;
for (auto symlinkDir : symlinkDirs) {
if (DIR* dp = ::opendir(symlinkDir)) {
while (struct dirent* ep = ::readdir(dp)) {
if (ep->d_type == DT_LNK) {
path = symlinkDir;
path += '/';
path += ep->d_name;
char* target = ::realpath(path.c_str(), nullptr);
if (target) {
wpi::StringRef fname = wpi::sys::path::filename(target);
unsigned int dev = 0;
if (fname.startswith("video") &&
!fname.substr(5).getAsInteger(10, dev) && dev < retval.size()) {
retval[dev].otherPaths.emplace_back(path.str());
}
std::free(target);
}
}
}
::closedir(dp);
}
}
// remove devices with empty names
retval.erase(
std::remove_if(retval.begin(), retval.end(),
[](const UsbCameraInfo& x) { return x.name.empty(); }),
retval.end());
return retval;
}

View File

@@ -26,6 +26,11 @@ std::string GetUsbCameraPath(CS_Source source, CS_Status* status) {
return std::string{};
}
UsbCameraInfo GetUsbCameraInfo(CS_Source source, CS_Status* status) {
*status = CS_INVALID_HANDLE;
return UsbCameraInfo{};
}
std::vector<UsbCameraInfo> EnumerateUsbCameras(CS_Status* status) {
*status = CS_INVALID_HANDLE;
return std::vector<UsbCameraInfo>{};

View File

@@ -57,6 +57,7 @@
static constexpr int NewImageMessage = 0x0400 + 4488;
static constexpr int SetCameraMessage = 0x0400 + 254;
static constexpr int WaitForStartupMessage = 0x0400 + 294;
static constexpr int PumpReadyMessage = 0x0400 + 330;
static constexpr char const* kPropWbValue = "WhiteBalance";
static constexpr char const* kPropExValue = "Exposure";
@@ -196,6 +197,7 @@ void UsbCameraImpl::Start() {
[this](HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
return this->PumpMain(hwnd, uiMsg, wParam, lParam);
});
m_messagePump->PostWindowMessage(PumpReadyMessage, nullptr, nullptr);
}
void UsbCameraImpl::PostRequestNewFrame() {
@@ -347,7 +349,7 @@ LRESULT UsbCameraImpl::PumpMain(HWND hwnd, UINT uiMsg, WPARAM wParam,
}
m_imageCallback.Reset();
break;
case WM_CREATE:
case PumpReadyMessage:
// Pump Created and ready to go
DeviceConnect();
break;
@@ -1020,4 +1022,17 @@ std::string GetUsbCameraPath(CS_Source source, CS_Status* status) {
return static_cast<UsbCameraImpl&>(*data->source).GetPath();
}
UsbCameraInfo GetUsbCameraInfo(CS_Source source, CS_Status* status) {
UsbCameraInfo info;
auto data = Instance::GetInstance().GetSource(source);
if (!data || data->kind != CS_SOURCE_USB) {
*status = CS_INVALID_HANDLE;
return info;
}
info.path = static_cast<UsbCameraImpl&>(*data->source).GetPath();
// TODO: dev and name
return info;
}
} // namespace cs

View File

@@ -82,3 +82,4 @@ kResourceType_DigilentDMC60 = 80
kResourceType_PWMVictorSPX = 81
kResourceType_RevSparkMaxPWM = 82
kResourceType_RevSparkMaxCAN = 83
kResourceType_ADIS16470 = 84

View File

@@ -75,6 +75,9 @@ public class DriverStationSim {
public void setDsAttached(boolean dsAttached) {
DriverStationDataJNI.setDsAttached(dsAttached);
}
public void notifyNewData() {
DriverStationDataJNI.notifyNewData();
}
public void resetData() {
DriverStationDataJNI.resetData();

View File

@@ -22,7 +22,7 @@ using namespace hal;
namespace {
struct Receives {
uint64_t lastTimeStamp;
uint32_t lastTimeStamp;
uint8_t data[8];
uint8_t length;
};
@@ -40,30 +40,13 @@ struct CANStorage {
static UnlimitedHandleResource<HAL_CANHandle, CANStorage, HAL_HandleEnum::CAN>*
canHandles;
static std::atomic_bool HasFixedTime{false};
static uint64_t timeSpanDiff;
static void CheckDeltaTime() {
if (HasFixedTime) return;
HasFixedTime = true;
// TODO: Fix locking
static uint32_t GetPacketBaseTime() {
timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
int32_t status = 0;
uint64_t fpgaTime = HAL_GetFPGATime(&status);
// Convert t to microseconds
uint64_t us = t.tv_sec * 1000000 + t.tv_nsec / 1000;
timeSpanDiff =
us - fpgaTime; // This assumes CLOCK_MONOTONIC is greater then FPGA Time.
}
static inline uint64_t ConvertToFPGATime(uint32_t canMs) {
uint64_t canMsToUs = canMs * 1000;
return canMsToUs - timeSpanDiff;
// Convert t to milliseconds
uint64_t ms = t.tv_sec * 1000ull + t.tv_nsec / 1000000ull;
return ms & 0xFFFFFFFF;
}
namespace hal {
@@ -89,7 +72,6 @@ HAL_CANHandle HAL_InitializeCAN(HAL_CANManufacturer manufacturer,
int32_t deviceId, HAL_CANDeviceType deviceType,
int32_t* status) {
hal::init::CheckInit();
CheckDeltaTime();
auto can = std::make_shared<CANStorage>();
auto handle = canHandles->Allocate(can);
@@ -189,17 +171,16 @@ void HAL_ReadCANPacketNew(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
uint32_t ts = 0;
HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
uint64_t timestamp = ConvertToFPGATime(ts);
if (*status == 0) {
std::lock_guard<wpi::mutex> lock(can->mapMutex);
auto& msg = can->receives[messageId];
msg.length = dataSize;
msg.lastTimeStamp = timestamp;
msg.lastTimeStamp = ts;
// The NetComm call placed in data, copy into the msg
std::memcpy(msg.data, data, dataSize);
}
*length = dataSize;
*receivedTimestamp = timestamp;
*receivedTimestamp = ts;
}
void HAL_ReadCANPacketLatest(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
@@ -216,21 +197,21 @@ void HAL_ReadCANPacketLatest(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
uint32_t ts = 0;
HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
uint64_t timestamp = ConvertToFPGATime(ts);
std::lock_guard<wpi::mutex> lock(can->mapMutex);
if (*status == 0) {
// fresh update
auto& msg = can->receives[messageId];
msg.length = dataSize;
*length = dataSize;
msg.lastTimeStamp = timestamp;
*receivedTimestamp = timestamp;
msg.lastTimeStamp = ts;
*receivedTimestamp = ts;
// The NetComm call placed in data, copy into the msg
std::memcpy(msg.data, data, dataSize);
} else {
auto i = can->receives.find(messageId);
if (i != can->receives.end()) {
std::memcpy(i->second.data, data, i->second.length);
// Read the data from the stored message into the output
std::memcpy(data, i->second.data, i->second.length);
*length = i->second.length;
*receivedTimestamp = i->second.lastTimeStamp;
*status = 0;
@@ -253,29 +234,28 @@ void HAL_ReadCANPacketTimeout(HAL_CANHandle handle, int32_t apiId,
uint32_t ts = 0;
HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
uint64_t timestamp = ConvertToFPGATime(ts);
std::lock_guard<wpi::mutex> lock(can->mapMutex);
if (*status == 0) {
// fresh update
auto& msg = can->receives[messageId];
msg.length = dataSize;
*length = dataSize;
msg.lastTimeStamp = timestamp;
*receivedTimestamp = timestamp;
msg.lastTimeStamp = ts;
*receivedTimestamp = ts;
// The NetComm call placed in data, copy into the msg
std::memcpy(msg.data, data, dataSize);
} else {
auto i = can->receives.find(messageId);
if (i != can->receives.end()) {
// Found, check if new enough
uint64_t now = HAL_GetFPGATime(status);
if (now - i->second.lastTimeStamp >
static_cast<uint64_t>(timeoutMs) * 1000) {
uint32_t now = GetPacketBaseTime();
if (now - i->second.lastTimeStamp > static_cast<uint32_t>(timeoutMs)) {
// Timeout, return bad status
*status = HAL_CAN_TIMEOUT;
return;
}
std::memcpy(i->second.data, data, i->second.length);
// Read the data from the stored message into the output
std::memcpy(data, i->second.data, i->second.length);
*length = i->second.length;
*receivedTimestamp = i->second.lastTimeStamp;
*status = 0;
@@ -299,14 +279,15 @@ void HAL_ReadCANPeriodicPacket(HAL_CANHandle handle, int32_t apiId,
std::lock_guard<wpi::mutex> lock(can->mapMutex);
auto i = can->receives.find(messageId);
if (i != can->receives.end()) {
uint64_t now = HAL_GetFPGATime(status);
// Found, check if new enough
if (now - i->second.lastTimeStamp <
static_cast<uint64_t>(periodMs) * 1000) {
*status = 0;
std::memcpy(i->second.data, data, i->second.length);
uint32_t now = GetPacketBaseTime();
if (now - i->second.lastTimeStamp < static_cast<uint32_t>(periodMs)) {
// Read the data from the stored message into the output
std::memcpy(data, i->second.data, i->second.length);
*length = i->second.length;
*receivedTimestamp = i->second.lastTimeStamp;
*status = 0;
return;
}
}
}
@@ -315,29 +296,28 @@ void HAL_ReadCANPeriodicPacket(HAL_CANHandle handle, int32_t apiId,
uint32_t ts = 0;
HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
uint64_t timestamp = ConvertToFPGATime(ts);
std::lock_guard<wpi::mutex> lock(can->mapMutex);
if (*status == 0) {
// fresh update
auto& msg = can->receives[messageId];
msg.length = dataSize;
*length = dataSize;
msg.lastTimeStamp = timestamp;
*receivedTimestamp = timestamp;
msg.lastTimeStamp = ts;
*receivedTimestamp = ts;
// The NetComm call placed in data, copy into the msg
std::memcpy(msg.data, data, dataSize);
} else {
auto i = can->receives.find(messageId);
if (i != can->receives.end()) {
// Found, check if new enough
uint64_t now = HAL_GetFPGATime(status);
if (now - i->second.lastTimeStamp >
static_cast<uint64_t>(timeoutMs) * 1000) {
uint32_t now = GetPacketBaseTime();
if (now - i->second.lastTimeStamp > static_cast<uint32_t>(timeoutMs)) {
// Timeout, return bad status
*status = HAL_CAN_TIMEOUT;
return;
}
std::memcpy(i->second.data, data, i->second.length);
// Read the data from the stored message into the output
std::memcpy(data, i->second.data, i->second.length);
*length = i->second.length;
*receivedTimestamp = i->second.lastTimeStamp;
*status = 0;

View File

@@ -239,7 +239,7 @@ uint64_t HAL_GetFPGATime(int32_t* status) {
*status = NiFpga_Status_ResourceNotInitialized;
return 0;
}
*status = 0;
uint64_t upper1 = global->readLocalTimeUpper(status);
uint32_t lower = global->readLocalTime(status);
uint64_t upper2 = global->readLocalTimeUpper(status);

View File

@@ -31,7 +31,7 @@ static constexpr int32_t StatusEnergy = 0x5D;
static constexpr int32_t Control1 = 0x70;
static constexpr int32_t TimeoutMs = 50;
static constexpr int32_t TimeoutMs = 100;
static constexpr int32_t StatusPeriodMs = 25;
/* encoder/decoders */

View File

@@ -215,9 +215,11 @@ void SerialHelper::QueryHubPaths(int32_t* status) {
if (matchString.equals(devNameRef)) continue;
// Search directories to get a list of system accessors
// The directories we need are not symbolic, so we can safely
// disable symbolic links.
std::error_code ec;
for (auto p = wpi::sys::fs::recursive_directory_iterator(
"/sys/devices/soc0", ec);
"/sys/devices/soc0", ec, false);
p != wpi::sys::fs::recursive_directory_iterator(); p.increment(ec)) {
if (ec) break;
wpi::StringRef path{p->path()};

View File

@@ -103,6 +103,8 @@ class DriverStationSim {
HALSIM_SetDriverStationDsAttached(dsAttached);
}
void NotifyNewData() { HALSIM_NotifyDriverStationNewData(); }
void ResetData() { HALSIM_ResetDriverStationData(); }
};
} // namespace sim

View File

@@ -41,13 +41,11 @@ struct CANStorage {
static UnlimitedHandleResource<HAL_CANHandle, CANStorage, HAL_HandleEnum::CAN>*
canHandles;
static void CheckDeltaTime() {
// Noop on sim
}
static inline uint64_t ConvertToFPGATime(uint32_t canMs) {
uint64_t canMsToUs = canMs * 1000;
return canMsToUs;
static uint32_t GetPacketBaseTime() {
int status = 0;
auto basetime = HAL_GetFPGATime(&status);
// us to ms
return (basetime / 1000ull) & 0xFFFFFFFF;
}
namespace hal {
@@ -83,7 +81,6 @@ HAL_CANHandle HAL_InitializeCAN(HAL_CANManufacturer manufacturer,
int32_t deviceId, HAL_CANDeviceType deviceType,
int32_t* status) {
hal::init::CheckInit();
CheckDeltaTime();
auto can = std::make_shared<CANStorage>();
auto handle = canHandles->Allocate(can);
@@ -183,17 +180,16 @@ void HAL_ReadCANPacketNew(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
uint32_t ts = 0;
HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
uint64_t timestamp = ConvertToFPGATime(ts);
if (*status == 0) {
std::lock_guard<wpi::mutex> lock(can->mapMutex);
auto& msg = can->receives[messageId];
msg.length = dataSize;
msg.lastTimeStamp = timestamp;
msg.lastTimeStamp = ts;
// The NetComm call placed in data, copy into the msg
std::memcpy(msg.data, data, dataSize);
}
*length = dataSize;
*receivedTimestamp = timestamp;
*receivedTimestamp = ts;
}
void HAL_ReadCANPacketLatest(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
@@ -210,21 +206,21 @@ void HAL_ReadCANPacketLatest(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
uint32_t ts = 0;
HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
uint64_t timestamp = ConvertToFPGATime(ts);
std::lock_guard<wpi::mutex> lock(can->mapMutex);
if (*status == 0) {
// fresh update
auto& msg = can->receives[messageId];
msg.length = dataSize;
*length = dataSize;
msg.lastTimeStamp = timestamp;
*receivedTimestamp = timestamp;
msg.lastTimeStamp = ts;
*receivedTimestamp = ts;
// The NetComm call placed in data, copy into the msg
std::memcpy(msg.data, data, dataSize);
} else {
auto i = can->receives.find(messageId);
if (i != can->receives.end()) {
std::memcpy(i->second.data, data, i->second.length);
// Read the data from the stored message into the output
std::memcpy(data, i->second.data, i->second.length);
*length = i->second.length;
*receivedTimestamp = i->second.lastTimeStamp;
*status = 0;
@@ -247,29 +243,28 @@ void HAL_ReadCANPacketTimeout(HAL_CANHandle handle, int32_t apiId,
uint32_t ts = 0;
HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
uint64_t timestamp = ConvertToFPGATime(ts);
std::lock_guard<wpi::mutex> lock(can->mapMutex);
if (*status == 0) {
// fresh update
auto& msg = can->receives[messageId];
msg.length = dataSize;
*length = dataSize;
msg.lastTimeStamp = timestamp;
*receivedTimestamp = timestamp;
msg.lastTimeStamp = ts;
*receivedTimestamp = ts;
// The NetComm call placed in data, copy into the msg
std::memcpy(msg.data, data, dataSize);
} else {
auto i = can->receives.find(messageId);
if (i != can->receives.end()) {
// Found, check if new enough
uint64_t now = HAL_GetFPGATime(status);
if (now - i->second.lastTimeStamp >
static_cast<uint64_t>(timeoutMs) * 1000) {
uint32_t now = GetPacketBaseTime();
if (now - i->second.lastTimeStamp > static_cast<uint32_t>(timeoutMs)) {
// Timeout, return bad status
*status = HAL_CAN_TIMEOUT;
return;
}
std::memcpy(i->second.data, data, i->second.length);
// Read the data from the stored message into the output
std::memcpy(data, i->second.data, i->second.length);
*length = i->second.length;
*receivedTimestamp = i->second.lastTimeStamp;
*status = 0;
@@ -286,20 +281,22 @@ void HAL_ReadCANPeriodicPacket(HAL_CANHandle handle, int32_t apiId,
*status = HAL_HANDLE_ERROR;
return;
}
uint32_t messageId = CreateCANId(can.get(), apiId);
{
std::lock_guard<wpi::mutex> lock(can->mapMutex);
auto i = can->receives.find(messageId);
if (i != can->receives.end()) {
uint64_t now = HAL_GetFPGATime(status);
// Found, check if new enough
if (now - i->second.lastTimeStamp <
static_cast<uint64_t>(periodMs) * 1000) {
uint32_t now = GetPacketBaseTime();
if (now - i->second.lastTimeStamp < static_cast<uint32_t>(periodMs)) {
*status = 0;
std::memcpy(i->second.data, data, i->second.length);
// Read the data from the stored message into the output
std::memcpy(data, i->second.data, i->second.length);
*length = i->second.length;
*receivedTimestamp = i->second.lastTimeStamp;
return;
}
}
}
@@ -308,29 +305,28 @@ void HAL_ReadCANPeriodicPacket(HAL_CANHandle handle, int32_t apiId,
uint32_t ts = 0;
HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
uint64_t timestamp = ConvertToFPGATime(ts);
std::lock_guard<wpi::mutex> lock(can->mapMutex);
if (*status == 0) {
// fresh update
auto& msg = can->receives[messageId];
msg.length = dataSize;
*length = dataSize;
msg.lastTimeStamp = timestamp;
*receivedTimestamp = timestamp;
msg.lastTimeStamp = ts;
*receivedTimestamp = ts;
// The NetComm call placed in data, copy into the msg
std::memcpy(msg.data, data, dataSize);
} else {
auto i = can->receives.find(messageId);
if (i != can->receives.end()) {
// Found, check if new enough
uint64_t now = HAL_GetFPGATime(status);
if (now - i->second.lastTimeStamp >
static_cast<uint64_t>(timeoutMs) * 1000) {
uint32_t now = GetPacketBaseTime();
if (now - i->second.lastTimeStamp > static_cast<uint32_t>(timeoutMs)) {
// Timeout, return bad status
*status = HAL_CAN_TIMEOUT;
return;
}
std::memcpy(i->second.data, data, i->second.length);
// Read the data from the stored message into the output
std::memcpy(data, i->second.data, i->second.length);
*length = i->second.length;
*receivedTimestamp = i->second.lastTimeStamp;
*status = 0;

View File

@@ -41,7 +41,7 @@ void DriverStationData::ResetData() {
test.Reset(false);
eStop.Reset(false);
fmsAttached.Reset(false);
dsAttached.Reset(false);
dsAttached.Reset(true);
allianceStationId.Reset(static_cast<HAL_AllianceStationID>(0));
matchTime.Reset(0.0);

View File

@@ -63,7 +63,7 @@ class DriverStationData {
SimDataValue<HAL_Bool, MakeBoolean, GetTestName> test{false};
SimDataValue<HAL_Bool, MakeBoolean, GetEStopName> eStop{false};
SimDataValue<HAL_Bool, MakeBoolean, GetFmsAttachedName> fmsAttached{false};
SimDataValue<HAL_Bool, MakeBoolean, GetDsAttachedName> dsAttached{false};
SimDataValue<HAL_Bool, MakeBoolean, GetDsAttachedName> dsAttached{true};
SimDataValue<HAL_AllianceStationID, MakeAllianceStationIdValue,
GetAllianceStationIdName>
allianceStationId{static_cast<HAL_AllianceStationID>(0)};

View File

@@ -22,14 +22,15 @@ using namespace nt;
void Dispatcher::StartServer(const Twine& persist_filename,
const char* listen_address, unsigned int port) {
std::string listen_address_copy(StringRef(listen_address).trim());
DispatcherBase::StartServer(
persist_filename,
std::unique_ptr<wpi::NetworkAcceptor>(new wpi::TCPAcceptor(
static_cast<int>(port), listen_address, m_logger)));
static_cast<int>(port), listen_address_copy.c_str(), m_logger)));
}
void Dispatcher::SetServer(const char* server_name, unsigned int port) {
std::string server_name_copy(server_name);
std::string server_name_copy(StringRef(server_name).trim());
SetConnector([=]() -> std::unique_ptr<wpi::NetworkStream> {
return wpi::TCPConnector::connect(server_name_copy.c_str(),
static_cast<int>(port), m_logger, 1);
@@ -40,7 +41,7 @@ void Dispatcher::SetServer(
ArrayRef<std::pair<StringRef, unsigned int>> servers) {
wpi::SmallVector<std::pair<std::string, int>, 16> servers_copy;
for (const auto& server : servers)
servers_copy.emplace_back(std::string{server.first},
servers_copy.emplace_back(std::string{server.first.trim()},
static_cast<int>(server.second));
SetConnector([=]() -> std::unique_ptr<wpi::NetworkStream> {
@@ -94,7 +95,7 @@ void Dispatcher::SetServerTeam(unsigned int team, unsigned int port) {
}
void Dispatcher::SetServerOverride(const char* server_name, unsigned int port) {
std::string server_name_copy(server_name);
std::string server_name_copy(StringRef(server_name).trim());
SetConnectorOverride([=]() -> std::unique_ptr<wpi::NetworkStream> {
return wpi::TCPConnector::connect(server_name_copy.c_str(),
static_cast<int>(port), m_logger, 1);

View File

@@ -11,7 +11,7 @@ using namespace nt;
std::atomic<int> InstanceImpl::s_default{-1};
std::atomic<InstanceImpl*> InstanceImpl::s_fast_instances[10];
wpi::UidVector<std::unique_ptr<InstanceImpl>, 10> InstanceImpl::s_instances;
wpi::UidVector<InstanceImpl*, 10> InstanceImpl::s_instances;
wpi::mutex InstanceImpl::s_mutex;
using namespace std::placeholders;
@@ -54,7 +54,7 @@ InstanceImpl* InstanceImpl::Get(int inst) {
// vector
if (static_cast<unsigned int>(inst) < s_instances.size()) {
return s_instances[inst].get();
return s_instances[inst];
}
// doesn't exist
@@ -84,9 +84,9 @@ int InstanceImpl::Alloc() {
}
int InstanceImpl::AllocImpl() {
unsigned int inst = s_instances.emplace_back();
unsigned int inst = s_instances.emplace_back(nullptr);
InstanceImpl* ptr = new InstanceImpl(inst);
s_instances[inst].reset(ptr);
s_instances[inst] = ptr;
if (inst < (sizeof(s_fast_instances) / sizeof(s_fast_instances[0]))) {
s_fast_instances[inst] = ptr;
@@ -104,5 +104,6 @@ void InstanceImpl::Destroy(int inst) {
s_fast_instances[inst] = nullptr;
}
delete s_instances[inst];
s_instances.erase(inst);
}

View File

@@ -51,7 +51,7 @@ class InstanceImpl {
static std::atomic<int> s_default;
static std::atomic<InstanceImpl*> s_fast_instances[10];
static wpi::UidVector<std::unique_ptr<InstanceImpl>, 10> s_instances;
static wpi::UidVector<InstanceImpl*, 10> s_instances;
static wpi::mutex s_mutex;
};

View File

@@ -15,6 +15,8 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -112,16 +114,17 @@ class ConnectionListenerTest {
}
@Test
@ParameterizedTest
@DisabledOnOs(OS.WINDOWS)
@SuppressWarnings("PMD.AvoidUsingHardCodedIP")
void testThreaded() {
m_serverInst.startServer("connectionlistenertest.ini", "127.0.0.1", 10000);
@ValueSource(strings = { "127.0.0.1", "127.0.0.1 ", " 127.0.0.1 " })
void testThreaded(String address) {
m_serverInst.startServer("connectionlistenertest.ini", address, 10000);
List<ConnectionNotification> events = new ArrayList<>();
final int handle = m_serverInst.addConnectionListener(events::add, false);
// trigger a connect event
m_clientInst.startClient("127.0.0.1", 10000);
m_clientInst.startClient(address, 10000);
// wait for client to report it's started, then wait another 0.1 sec
try {

View File

@@ -100,9 +100,8 @@ sourceSets {
dev
}
compileJava {
sourceCompatibility = 1.8
targetCompatibility = 1.8
tasks.withType(JavaCompile).configureEach {
options.compilerArgs = ['--release', '8']
}
dependencies {

View File

@@ -9,7 +9,7 @@ project.netCommComponents.each { String s->
netCommLibConfigs[s] = ['linux:athena']
}
def niLibrariesVersion = '2019.9.3'
def niLibrariesVersion = '2019.12.1'
model {
dependencyConfigs {

View File

@@ -1,4 +1,4 @@
def opencvVersion = '3.4.4-3'
def opencvVersion = '3.4.4-4'
if (project.hasProperty('useCpp') && project.useCpp) {
model {

View File

@@ -13,6 +13,11 @@ class ADXRS450_SpiGyroWrapperTest
: public ::testing::TestWithParam<frc::SPI::Port> {};
TEST_P(ADXRS450_SpiGyroWrapperTest, TestAccelerometer) {
#ifdef __APPLE__
// Disable test on mac, because of unknown failures
// TODO: Finally figure out the isse.
return;
#else
const double EPSILON = .000001;
frc::SPI::Port port = GetParam();
@@ -26,6 +31,7 @@ TEST_P(ADXRS450_SpiGyroWrapperTest, TestAccelerometer) {
sim.SetAngle(32.56);
EXPECT_NEAR(32.56, sim.GetAngle(), EPSILON);
EXPECT_NEAR(32.56, gyro.GetAngle(), EPSILON);
#endif
}
INSTANTIATE_TEST_CASE_P(

View File

@@ -19,6 +19,7 @@
#include <wpi/StringRef.h>
#include "frc/AnalogInput.h"
#include "frc/MotorSafety.h"
#include "frc/Timer.h"
#include "frc/Utility.h"
#include "frc/WPIErrors.h"
@@ -548,10 +549,17 @@ void DriverStation::ReportJoystickUnpluggedWarning(const wpi::Twine& message) {
void DriverStation::Run() {
m_isRunning = true;
int safetyCounter = 0;
while (m_isRunning) {
HAL_WaitForDSData();
GetData();
if (IsDisabled()) safetyCounter = 0;
if (++safetyCounter >= 4) {
MotorSafety::CheckMotors();
safetyCounter = 0;
}
if (m_userInDisabled) HAL_ObserveUserProgramDisabled();
if (m_userInAutonomous) HAL_ObserveUserProgramAutonomous();
if (m_userInTeleop) HAL_ObserveUserProgramTeleop();

View File

@@ -10,53 +10,66 @@
#include <algorithm>
#include <utility>
#include <wpi/SmallPtrSet.h>
#include <wpi/SmallString.h>
#include <wpi/raw_ostream.h>
#include "frc/DriverStation.h"
#include "frc/WPIErrors.h"
using namespace frc;
MotorSafety::MotorSafety(MotorSafety&& rhs)
: ErrorBase(std::move(rhs)), m_enabled(std::move(rhs.m_enabled)) {
m_watchdog = Watchdog(rhs.m_watchdog.GetTimeout(), [this] { TimeoutFunc(); });
static wpi::SmallPtrSet<MotorSafety*, 32> instanceList;
static wpi::mutex listMutex;
MotorSafety::MotorSafety() {
std::lock_guard<wpi::mutex> lock(listMutex);
instanceList.insert(this);
}
MotorSafety::~MotorSafety() {
std::lock_guard<wpi::mutex> lock(listMutex);
instanceList.erase(this);
}
MotorSafety::MotorSafety(MotorSafety&& rhs)
: ErrorBase(std::move(rhs)),
m_expiration(std::move(rhs.m_expiration)),
m_enabled(std::move(rhs.m_enabled)),
m_stopTime(std::move(rhs.m_stopTime)) {}
MotorSafety& MotorSafety::operator=(MotorSafety&& rhs) {
ErrorBase::operator=(std::move(rhs));
m_watchdog = Watchdog(rhs.m_watchdog.GetTimeout(), [this] { TimeoutFunc(); });
m_expiration = std::move(rhs.m_expiration);
m_enabled = std::move(rhs.m_enabled);
m_stopTime = std::move(rhs.m_stopTime);
return *this;
}
void MotorSafety::Feed() {
std::lock_guard<wpi::mutex> lock(m_thisMutex);
m_watchdog.Reset();
m_stopTime = Timer::GetFPGATimestamp() + m_expiration;
}
void MotorSafety::SetExpiration(double expirationTime) {
std::lock_guard<wpi::mutex> lock(m_thisMutex);
m_watchdog.SetTimeout(expirationTime);
m_expiration = expirationTime;
}
double MotorSafety::GetExpiration() const {
std::lock_guard<wpi::mutex> lock(m_thisMutex);
return m_watchdog.GetTimeout();
return m_expiration;
}
bool MotorSafety::IsAlive() const {
std::lock_guard<wpi::mutex> lock(m_thisMutex);
return !m_enabled || !m_watchdog.IsExpired();
return !m_enabled || m_stopTime > Timer::GetFPGATimestamp();
}
void MotorSafety::SetSafetyEnabled(bool enabled) {
std::lock_guard<wpi::mutex> lock(m_thisMutex);
if (enabled) {
m_watchdog.Enable();
} else {
m_watchdog.Disable();
}
m_enabled = enabled;
}
@@ -65,16 +78,34 @@ bool MotorSafety::IsSafetyEnabled() const {
return m_enabled;
}
void MotorSafety::TimeoutFunc() {
auto& ds = DriverStation::GetInstance();
if (ds.IsDisabled() || ds.IsTest()) {
void MotorSafety::Check() {
bool enabled;
double stopTime;
{
std::lock_guard<wpi::mutex> lock(m_thisMutex);
enabled = m_enabled;
stopTime = m_stopTime;
}
DriverStation& ds = DriverStation::GetInstance();
if (!enabled || ds.IsDisabled() || ds.IsTest()) {
return;
}
wpi::SmallString<128> buf;
wpi::raw_svector_ostream desc(buf);
GetDescription(desc);
desc << "... Output not updated often enough.";
wpi_setWPIErrorWithContext(Timeout, desc.str());
StopMotor();
if (stopTime < Timer::GetFPGATimestamp()) {
wpi::SmallString<128> buf;
wpi::raw_svector_ostream desc(buf);
GetDescription(desc);
desc << "... Output not updated often enough.";
wpi_setWPIErrorWithContext(Timeout, desc.str());
StopMotor();
}
}
void MotorSafety::CheckMotors() {
std::lock_guard<wpi::mutex> lock(listMutex);
for (auto elem : instanceList) {
elem->Check();
}
}

View File

@@ -98,13 +98,15 @@ RobotBase::RobotBase() : m_ds(DriverStation::GetInstance()) {
SmartDashboard::init();
std::FILE* file = nullptr;
file = std::fopen("/tmp/frc_versions/FRC_Lib_Version.ini", "w");
if (IsReal()) {
std::FILE* file = nullptr;
file = std::fopen("/tmp/frc_versions/FRC_Lib_Version.ini", "w");
if (file != nullptr) {
std::fputs("C++ ", file);
std::fputs(GetWPILibVersion(), file);
std::fclose(file);
if (file != nullptr) {
std::fputs("C++ ", file);
std::fputs(GetWPILibVersion(), file);
std::fclose(file);
}
}
// First and one-time initialization

View File

@@ -51,15 +51,22 @@ void Watchdog::Thread::Main() {
auto now = hal::fpga_clock::now();
if (now - watchdog->m_lastTimeoutPrintTime > kMinPrintPeriod) {
watchdog->m_lastTimeoutPrintTime = now;
wpi::outs() << "Watchdog not fed within "
<< wpi::format("%.6f",
watchdog->m_timeout.count() / 1.0e6)
<< "s\n";
if (!watchdog->m_suppressTimeoutMessage) {
wpi::outs() << "Watchdog not fed within "
<< wpi::format("%.6f",
watchdog->m_timeout.count() / 1.0e6)
<< "s\n";
}
}
// Set expiration flag before calling the callback so any manipulation
// of the flag in the callback (e.g., calling Disable()) isn't
// clobbered.
watchdog->m_isExpired = true;
lock.unlock();
watchdog->m_callback();
lock.lock();
watchdog->m_isExpired = true;
}
// Otherwise, a Watchdog removed itself from the queue (it notifies the
// scheduler of this) or a spurious wakeup occurred, so just rewait with
@@ -159,6 +166,10 @@ void Watchdog::Disable() {
thr->m_cond.notify_all();
}
void Watchdog::SuppressTimeoutMessage(bool suppress) {
m_suppressTimeoutMessage = suppress;
}
bool Watchdog::operator>(const Watchdog& rhs) {
return m_expirationTime > rhs.m_expirationTime;
}

View File

@@ -0,0 +1,13 @@
/*----------------------------------------------------------------------------*/
/* 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

@@ -0,0 +1,35 @@
/*----------------------------------------------------------------------------*/
/* 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

@@ -0,0 +1,12 @@
/*----------------------------------------------------------------------------*/
/* 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/LayoutType.h"
using namespace frc;
wpi::StringRef LayoutType::GetLayoutName() const { return m_layoutName; }

View File

@@ -0,0 +1,46 @@
/*----------------------------------------------------------------------------*/
/* 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/SendableCameraWrapper.h"
#include <cscore_oo.h>
#include <wpi/DenseMap.h>
#include "frc/smartdashboard/SendableBuilder.h"
using namespace frc;
namespace {
constexpr const char* kProtocol = "camera_server://";
wpi::DenseMap<int, std::unique_ptr<SendableCameraWrapper>> wrappers;
} // namespace
SendableCameraWrapper& SendableCameraWrapper::Wrap(
const cs::VideoSource& source) {
return Wrap(source.GetHandle());
}
SendableCameraWrapper& SendableCameraWrapper::Wrap(CS_Source source) {
auto& wrapper = wrappers[static_cast<int>(source)];
if (!wrapper)
wrapper = std::make_unique<SendableCameraWrapper>(source, private_init{});
return *wrapper;
}
SendableCameraWrapper::SendableCameraWrapper(CS_Source source,
const private_init&)
: SendableBase(false), m_uri(kProtocol) {
CS_Status status = 0;
auto name = cs::GetSourceName(source, &status);
SetName(name);
m_uri += name;
}
void SendableCameraWrapper::InitSendable(SendableBuilder& builder) {
builder.AddStringProperty(".ShuffleboardURI", [this] { return m_uri; },
nullptr);
}

View File

@@ -11,6 +11,7 @@
#include <wpi/raw_ostream.h>
#include "frc/shuffleboard/ComplexWidget.h"
#include "frc/shuffleboard/SendableCameraWrapper.h"
#include "frc/shuffleboard/ShuffleboardComponent.h"
#include "frc/shuffleboard/ShuffleboardLayout.h"
#include "frc/shuffleboard/SimpleWidget.h"
@@ -25,8 +26,13 @@ ShuffleboardContainer::GetComponents() const {
return m_components;
}
ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& type,
const wpi::Twine& title) {
ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& title,
const LayoutType& type) {
return GetLayout(title, type.GetLayoutName());
}
ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& title,
const wpi::Twine& type) {
wpi::SmallVector<char, 16> storage;
auto titleRef = title.toStringRef(storage);
if (m_layouts.count(titleRef) == 0) {
@@ -38,6 +44,16 @@ ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& type,
return *m_layouts[titleRef];
}
ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& title) {
wpi::SmallVector<char, 16> storage;
auto titleRef = title.toStringRef(storage);
if (m_layouts.count(titleRef) == 0) {
wpi_setWPIErrorWithContext(
InvalidParameter, "No layout with the given title has been defined");
}
return *m_layouts[titleRef];
}
ComplexWidget& ShuffleboardContainer::Add(const wpi::Twine& title,
Sendable& sendable) {
CheckTitle(title);
@@ -47,6 +63,11 @@ ComplexWidget& ShuffleboardContainer::Add(const wpi::Twine& title,
return *ptr;
}
ComplexWidget& ShuffleboardContainer::Add(const wpi::Twine& title,
const cs::VideoSource& video) {
return Add(title, SendableCameraWrapper::Wrap(video));
}
ComplexWidget& ShuffleboardContainer::Add(Sendable& sendable) {
if (sendable.GetName().empty()) {
wpi::outs() << "Sendable must have a name\n";
@@ -54,6 +75,10 @@ ComplexWidget& ShuffleboardContainer::Add(Sendable& sendable) {
return Add(sendable.GetName(), sendable);
}
ComplexWidget& ShuffleboardContainer::Add(const cs::VideoSource& video) {
return Add(SendableCameraWrapper::Wrap(video));
}
SimpleWidget& ShuffleboardContainer::Add(
const wpi::Twine& title, std::shared_ptr<nt::Value> defaultValue) {
CheckTitle(title);

View File

@@ -0,0 +1,12 @@
/*----------------------------------------------------------------------------*/
/* 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/WidgetType.h"
using namespace frc;
wpi::StringRef WidgetType::GetWidgetName() const { return m_widgetName; }

View File

@@ -16,9 +16,9 @@ namespace frc {
/**
* Gamepad Interface.
*/
class WPI_DEPRECATED("Inherit directly from GenericHID instead.") GamepadBase
: public GenericHID {
class GamepadBase : public GenericHID {
public:
WPI_DEPRECATED("Inherit directly from GenericHID instead.")
explicit GamepadBase(int port);
virtual ~GamepadBase() = default;

View File

@@ -26,7 +26,8 @@ namespace frc {
*
* Init() functions -- each of the following functions is called once when the
* appropriate mode is entered:
* - DisabledInit() -- called only when first disabled
* - DisabledInit() -- called each and every time disabled is entered from
* another mode
* - AutonomousInit() -- called each and every time autonomous is entered from
* another mode
* - TeleopInit() -- called each and every time teleop is entered from

View File

@@ -16,9 +16,9 @@ namespace frc {
/**
* Joystick Interface.
*/
class WPI_DEPRECATED("Inherit directly from GenericHID instead.") JoystickBase
: public GenericHID {
class JoystickBase : public GenericHID {
public:
WPI_DEPRECATED("Inherit directly from GenericHID instead.")
explicit JoystickBase(int port);
virtual ~JoystickBase() = default;

View File

@@ -10,10 +10,8 @@
#include <wpi/mutex.h>
#include <wpi/raw_ostream.h>
#include "frc/DriverStation.h"
#include "frc/ErrorBase.h"
#include "frc/Timer.h"
#include "frc/Watchdog.h"
namespace frc {
@@ -25,8 +23,8 @@ namespace frc {
*/
class MotorSafety : public ErrorBase {
public:
MotorSafety() = default;
virtual ~MotorSafety() = default;
MotorSafety();
virtual ~MotorSafety();
MotorSafety(MotorSafety&& rhs);
MotorSafety& operator=(MotorSafety&& rhs);
@@ -77,20 +75,39 @@ class MotorSafety : public ErrorBase {
*/
bool IsSafetyEnabled() const;
/**
* Check if this motor has exceeded its timeout.
*
* This method is called periodically to determine if this motor has exceeded
* its timeout value. If it has, the stop method is called, and the motor is
* shut down until its value is updated again.
*/
void Check();
/**
* Check the motors to see if any have timed out.
*
* This static method is called periodically to poll all the motors and stop
* any that have timed out.
*/
static void CheckMotors();
virtual void StopMotor() = 0;
virtual void GetDescription(wpi::raw_ostream& desc) const = 0;
private:
static constexpr double kDefaultSafetyExpiration = 0.1;
Watchdog m_watchdog{kDefaultSafetyExpiration, [this] { TimeoutFunc(); }};
// The expiration time for this object
double m_expiration = kDefaultSafetyExpiration;
// True if motor safety is enabled for this motor
bool m_enabled = false;
mutable wpi::mutex m_thisMutex;
// The FPGA clock value when the motor has expired
double m_stopTime = Timer::GetFPGATimestamp();
void TimeoutFunc();
mutable wpi::mutex m_thisMutex;
};
} // namespace frc

View File

@@ -19,6 +19,8 @@ enum class PIDSourceType { kDisplacement, kRate };
*/
class PIDSource {
public:
virtual ~PIDSource() = default;
/**
* Set which parameter you are using as a process control variable.
*

View File

@@ -13,11 +13,7 @@
namespace frc {
class WPI_DEPRECATED(
"WARNING: While it may look like a good choice to use for your code if "
"you're inexperienced, don't. Unless you know what you are doing, complex "
"code will be much more difficult under this system. Use TimedRobot or "
"Command-Based instead.") SampleRobot : public RobotBase {
class SampleRobot : public RobotBase {
public:
/**
* Start a competition.
@@ -95,6 +91,11 @@ class WPI_DEPRECATED(
virtual void RobotMain();
protected:
WPI_DEPRECATED(
"WARNING: While it may look like a good choice to use for your code if "
"you're inexperienced, don't. Unless you know what you are doing, "
"complex code will be much more difficult under this system. Use "
"TimedRobot or Command-Based instead.")
SampleRobot();
virtual ~SampleRobot() = default;

View File

@@ -82,7 +82,7 @@ class SerialPort : public ErrorBase {
* @param stopBits The number of stop bits to use as defined by the enum
* StopBits.
*/
WPI_DEPRECATED("Will be removed for 2019")
WPI_DEPRECATED("Will be removed for 2020")
SerialPort(int baudRate, const wpi::Twine& portName, Port port = kOnboard,
int dataBits = 8, Parity parity = kParity_None,
StopBits stopBits = kStopBits_One);

View File

@@ -75,6 +75,7 @@ S(SmartDashboardMissingKey, -43, "SmartDashboard data does not exist");
S(CommandIllegalUse, -50, "Illegal use of Command");
S(UnsupportedInSimulation, -80, "Unsupported in simulation");
S(CameraServerError, -90, "CameraServer error");
S(InvalidParameter, -100, "Invalid parameter value");
// Warnings
S(SampleRateTooHigh, 1, "Analog module sample rate is too high");

View File

@@ -104,6 +104,16 @@ class Watchdog {
*/
void Disable();
/**
* Enable or disable suppression of the generic timeout message.
*
* This may be desirable if the user-provided callback already prints a more
* specific message.
*
* @param suppress Whether to suppress generic timeout message.
*/
void SuppressTimeoutMessage(bool suppress);
private:
// Used for timeout print rate-limiting
static constexpr std::chrono::milliseconds kMinPrintPeriod{1000};
@@ -118,6 +128,8 @@ class Watchdog {
wpi::StringMap<std::chrono::microseconds> m_epochs;
bool m_isExpired = false;
bool m_suppressTimeoutMessage = false;
class Thread;
wpi::SafeThreadOwner<Thread>* m_owner;

View File

@@ -19,9 +19,9 @@ namespace frc {
* Live Window Sendable is a special type of object sendable to the live window.
* @deprecated Use Sendable directly instead
*/
class WPI_DEPRECATED("use Sendable directly instead") LiveWindowSendable
: public Sendable {
class LiveWindowSendable : public Sendable {
public:
WPI_DEPRECATED("use Sendable directly instead")
LiveWindowSendable() = default;
LiveWindowSendable(LiveWindowSendable&&) = default;
LiveWindowSendable& operator=(LiveWindowSendable&&) = default;

View File

@@ -0,0 +1,53 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#pragma once
#include "frc/shuffleboard/LayoutType.h"
namespace frc {
/**
* The types of layouts bundled with Shuffleboard.
*
* <pre>{@code
* ShuffleboardLayout myList = Shuffleboard::GetTab("My Tab")
* .GetLayout(BuiltinLayouts::kList, "My List");
* }</pre>
*/
class BuiltInLayouts {
public:
/**
* 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>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Label position</td><td>String</td><td>"BOTTOM"</td>
* <td>The position of component labels inside the grid. One of
* {@code ["TOP", "LEFT", "BOTTOM", "RIGHT", "HIDDEN"}</td></tr>
* </table>
*/
static const LayoutType kList;
/**
* Groups components in an <i>n</i> x <i>m</i> grid. Grid layouts default to
* 3x3. <br>Custom properties: <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Number of columns</td><td>Number</td><td>3</td><td>Must be in the
* range [1,15]</td>
* </tr>
* <tr><td>Number of rows</td><td>Number</td><td>3</td><td>Must be in the
* range [1,15]</td></tr> <tr> <td>Label position</td> <td>String</td>
* <td>"BOTTOM"</td>
* <td>The position of component labels inside the grid.
* One of {@code ["TOP", "LEFT", "BOTTOM", "RIGHT", "HIDDEN"}</td>
* </tr>
* </table>
*/
static const LayoutType kGrid;
};
} // namespace frc

View File

@@ -0,0 +1,387 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#pragma once
#include "frc/shuffleboard/WidgetType.h"
namespace frc {
/**
* The types of the widgets bundled with Shuffleboard.
*
* <p>For example, setting a number to be displayed with a slider:
* <pre>{@code
* NetworkTableEntry example = Shuffleboard.getTab("My Tab")
* .add("My Number", 0)
* .withWidget(BuiltInWidgets.kNumberSlider)
* .getEntry();
* }</pre>
*
* <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:
/**
* Displays a value with a simple text field.
* <br>Supported types:
* <ul>
* <li>String</li>
* <li>Number</li>
* <li>Boolean</li>
* </ul>
* <br>This widget has no custom properties.
*/
static const WidgetType kTextView;
/**
* Displays a number with a controllable slider.
* <br>Supported types:
* <ul>
* <li>Number</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Min</td><td>Number</td><td>-1.0</td><td>The minimum value of the
* slider</td></tr> <tr><td>Max</td><td>Number</td><td>1.0</td><td>The maximum
* value of the slider</td></tr> <tr><td>Block
* increment</td><td>Number</td><td>0.0625</td> <td>How much to move the
* slider by with the arrow keys</td></tr>
* </table>
*/
static const WidgetType kNumberSlider;
/**
* Displays a number with a view-only bar.
* <br>Supported types:
* <ul>
* <li>Number</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Min</td><td>Number</td><td>-1.0</td><td>The minimum value of the
* bar</td></tr> <tr><td>Max</td><td>Number</td><td>1.0</td><td>The maximum
* value of the bar</td></tr>
* <tr><td>Center</td><td>Number</td><td>0</td><td>The center ("zero") value
* of the bar</td></tr>
* </table>
*/
static const WidgetType kNumberBar;
/**
* Displays a number with a view-only dial. Displayed values are rounded to
* the nearest integer. <br>Supported types: <ul> <li>Number</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Min</td><td>Number</td><td>0</td><td>The minimum value of the
* dial</td></tr> <tr><td>Max</td><td>Number</td><td>100</td><td>The maximum
* value of the dial</td></tr> <tr><td>Show
* value</td><td>Boolean</td><td>true</td> <td>Whether or not to show the
* value as text</td></tr>
* </table>
*/
static const WidgetType 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
* points to a minimum. Making the widget smaller also helps with performance,
* but may cause the graph to become difficult to read. <br>Supported types:
* <ul>
* <li>Number</li>
* <li>Number array</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Visible time</td><td>Number</td><td>30</td>
* <td>How long, in seconds, should past data be visible for</td></tr>
* </table>
*/
static const WidgetType kGraph;
/**
* Displays a boolean value as a large colored box.
* <br>Supported types:
* <ul>
* <li>Boolean</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Color when true</td><td>Color</td><td>"green"</td>
* <td>Can be specified as a string ({@code "#00FF00"}) or a rgba integer
* ({@code 0x00FF0000})
* </td></tr>
* <tr><td>Color when false</td><td>Color</td><td>"red"</td>
* <td>Can be specified as a string or a number</td></tr>
* </table>
*/
static const WidgetType kBooleanBox;
/**
* Displays a boolean with a large interactive toggle button.
* <br>Supported types:
* <ul>
* <li>Boolean</li>
* </ul>
* <br>This widget has no custom properties.
*/
static const WidgetType kToggleButton;
/**
* Displays a boolean with a fixed-size toggle switch.
* <br>Supported types:
* <ul>
* <li>Boolean</li>
* </ul>
* <br>This widget has no custom properties.
*/
static const WidgetType kToggleSwitch;
/**
* Displays an analog input or a raw number with a number bar.
* <br>Supported types:
* <ul>
* <li>Number</li>
* <li>{@link edu.wpi.first.wpilibj.AnalogInput}</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Min</td><td>Number</td><td>0</td><td>The minimum value of the
* bar</td></tr> <tr><td>Max</td><td>Number</td><td>5</td><td>The maximum
* value of the bar</td></tr>
* <tr><td>Center</td><td>Number</td><td>0</td><td>The center ("zero") value
* of the bar</td></tr>
* <tr><td>Orientation</td><td>String</td><td>"HORIZONTAL"</td>
* <td>The orientation of the bar. One of {@code ["HORIZONTAL",
* "VERTICAL"]}</td></tr> <tr><td>Number of tick
* marks</td><td>Number</td><td>5</td> <td>The number of discrete ticks on the
* bar</td></tr>
* </table>
*/
static const WidgetType kVoltageView;
/**
* Displays a {@link edu.wpi.first.wpilibj.PowerDistributionPanel
* PowerDistributionPanel}. <br>Supported types: <ul> <li>{@link
* edu.wpi.first.wpilibj.PowerDistributionPanel}</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Show voltage and current values</td><td>Boolean</td><td>true</td>
* <td>Whether or not to display the voltage and current draw</td></tr>
* </table>
*/
static const WidgetType kPowerDistributionPanel;
/**
* Displays a {@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser
* SendableChooser} with a dropdown combo box with a list of options.
* <br>Supported types:
* <ul>
* <li>{@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser}</li>
* </ul>
* <br>This widget has no custom properties.
*/
static const WidgetType kComboBoxChooser;
/**
* Displays a {@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser
* SendableChooser} with a toggle button for each available option.
* <br>Supported types:
* <ul>
* <li>{@link edu.wpi.first.wpilibj.smartdashboard.SendableChooser}</li>
* </ul>
* <br>This widget has no custom properties.
*/
static const WidgetType kSplitButtonChooser;
/**
* Displays an {@link edu.wpi.first.wpilibj.Encoder} displaying its speed,
* total travelled distance, and its distance per tick. <br>Supported types:
* <ul>
* <li>{@link edu.wpi.first.wpilibj.Encoder}</li>
* </ul>
* <br>This widget has no custom properties.
*/
static const WidgetType kEncoder;
/**
* Displays a {@link edu.wpi.first.wpilibj.SpeedController SpeedController}.
* The speed controller will be controllable from the dashboard when test mode
* is enabled, but will otherwise be view-only. <br>Supported types: <ul>
* <li>{@link edu.wpi.first.wpilibj.PWMSpeedController}</li>
* <li>{@link edu.wpi.first.wpilibj.DMC60}</li>
* <li>{@link edu.wpi.first.wpilibj.Jaguar}</li>
* <li>{@link edu.wpi.first.wpilibj.PWMTalonSRX}</li>
* <li>{@link edu.wpi.first.wpilibj.PWMVictorSPX}</li>
* <li>{@link edu.wpi.first.wpilibj.SD540}</li>
* <li>{@link edu.wpi.first.wpilibj.Spark}</li>
* <li>{@link edu.wpi.first.wpilibj.Talon}</li>
* <li>{@link edu.wpi.first.wpilibj.Victor}</li>
* <li>{@link edu.wpi.first.wpilibj.VictorSP}</li>
* <li>{@link edu.wpi.first.wpilibj.SpeedControllerGroup}</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Orientation</td><td>String</td><td>"HORIZONTAL"</td>
* <td>One of {@code ["HORIZONTAL", "VERTICAL"]}</td></tr>
* </table>
*/
static const WidgetType kSpeedController;
/**
* Displays a command with a toggle button. Pressing the button will start the
* command, and the button will automatically release when the command
* completes. <br>Supported types: <ul> <li>{@link
* edu.wpi.first.wpilibj.command.Command}</li> <li>{@link
* edu.wpi.first.wpilibj.command.CommandGroup}</li> <li>Any custom subclass of
* {@code Command} or {@code CommandGroup}</li>
* </ul>
* <br>This widget has no custom properties.
*/
static const WidgetType 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
* will automatically deselect when the command completes. <br>Supported
* types: <ul> <li>{@link edu.wpi.first.wpilibj.command.PIDCommand}</li>
* <li>Any custom subclass of {@code PIDCommand}</li>
* </ul>
* <br>This widget has no custom properties.
*/
static const WidgetType kPIDCommand;
/**
* Displays a PID controller with an editor for the PIDF constants and a
* toggle switch for enabling and disabling the controller. <br>Supported
* types: <ul> <li>{@link edu.wpi.first.wpilibj.PIDController}</li>
* </ul>
* <br>This widget has no custom properties.
*/
static const WidgetType kPIDController;
/**
* Displays an accelerometer with a number bar displaying the magnitude of the
* acceleration and text displaying the exact value. <br>Supported types: <ul>
* <li>{@link edu.wpi.first.wpilibj.AnalogAccelerometer}</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Min</td><td>Number</td><td>-1</td>
* <td>The minimum acceleration value to display</td></tr>
* <tr><td>Max</td><td>Number</td><td>1</td>
* <td>The maximum acceleration value to display</td></tr>
* <tr><td>Show text</td><td>Boolean</td><td>true</td>
* <td>Show or hide the acceleration values</td></tr>
* <tr><td>Precision</td><td>Number</td><td>2</td>
* <td>How many numbers to display after the decimal point</td></tr>
* <tr><td>Show tick marks</td><td>Boolean</td><td>false</td>
* <td>Show or hide the tick marks on the number bars</td></tr>
* </table>
*/
static const WidgetType kAccelerometer;
/**
* Displays a 3-axis accelerometer with a number bar for each axis'
* accleration. <br>Supported types: <ul> <li>{@link
* edu.wpi.first.wpilibj.ADXL345_I2C}</li> <li>{@link
* edu.wpi.first.wpilibj.ADXL345_SPI}</li> <li>{@link
* edu.wpi.first.wpilibj.ADXL362}</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Range</td><td>{@link Range}</td><td>k16G</td><td>The accelerometer
* range</td></tr> <tr><td>Show value</td><td>Boolean</td><td>true</td>
* <td>Show or hide the acceleration values</td></tr>
* <tr><td>Precision</td><td>Number</td><td>2</td>
* <td>How many numbers to display after the decimal point</td></tr>
* <tr><td>Show tick marks</td><td>Boolean</td><td>false</td>
* <td>Show or hide the tick marks on the number bars</td></tr>
* </table>
*/
static const WidgetType k3AxisAccelerometer;
/**
* Displays a gyro with a dial from 0 to 360 degrees.
* <br>Supported types:
* <ul>
* <li>{@link edu.wpi.first.wpilibj.ADXRS450_Gyro}</li>
* <li>{@link edu.wpi.first.wpilibj.AnalogGyro}</li>
* <li>Any custom subclass of {@code GyroBase} (such as a MXP gyro)</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Major tick
* spacing</td><td>Number</td><td>45</td><td>Degrees</td></tr>
* <tr><td>Starting angle</td><td>Number</td><td>180</td>
* <td>How far to rotate the entire dial, in degrees</td></tr>
* <tr><td>Show tick mark ring</td><td>Boolean</td><td>true</td></tr>
* </table>
*/
static const WidgetType kGyro;
/**
* Displays a relay with toggle buttons for each supported mode (off, on,
* forward, reverse). <br>Supported types: <ul> <li>{@link
* edu.wpi.first.wpilibj.Relay}</li>
* </ul>
* <br>This widget has no custom properties.
*/
static const WidgetType 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
* drivebase. The widget will be controllable if the robot is in test mode.
* <br>Supported types:
* <ul>
* <li>{@link edu.wpi.first.wpilibj.drive.DifferentialDrive}</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Number of wheels</td><td>Number</td><td>4</td><td>Must be a
* positive even integer
* </td></tr>
* <tr><td>Wheel diameter</td><td>Number</td><td>80</td><td>Pixels</td></tr>
* <tr><td>Show velocity vectors</td><td>Boolean</td><td>true</td></tr>
* </table>
*/
static const WidgetType 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
* widget will be controllable if the robot is in test mode. <br>Supported
* types: <ul> <li>{@link edu.wpi.first.wpilibj.drive.MecanumDrive}</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Show velocity vectors</td><td>Boolean</td><td>true</td></tr>
* </table>
*/
static const WidgetType kMecanumDrive;
/**
* Displays a camera stream.
* <br>Supported types:
* <ul>
* <li>{@link edu.wpi.cscore.VideoSource} (as long as it is streaming on an
* MJPEG server)</li>
* </ul>
* <br>Custom properties:
* <table>
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></tr>
* <tr><td>Show crosshair</td><td>Boolean</td><td>true</td>
* <td>Show or hide a crosshair on the image</td></tr>
* <tr><td>Crosshair color</td><td>Color</td><td>"white"</td>
* <td>Can be a string or a rgba integer</td></tr>
* <tr><td>Show controls</td><td>Boolean</td><td>true</td><td>Show or hide the
* stream controls
* </td></tr>
* <tr><td>Rotation</td><td>String</td><td>"NONE"</td>
* <td>Rotates the displayed image. One of {@code ["NONE", "QUARTER_CW",
* "QUARTER_CCW", "HALF"]}
* </td></tr>
* </table>
*/
static const WidgetType kCameraStream;
};
} // namespace frc

View File

@@ -0,0 +1,36 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#pragma once
#include <wpi/Twine.h>
namespace frc {
/**
* Represents the type of a layout in Shuffleboard. Using this is preferred over
* specifying raw strings, to avoid typos and having to know or look up the
* exact string name for a desired layout.
*
* @see BuiltInLayouts the built-in layout types
*/
class LayoutType {
public:
explicit LayoutType(const char* layoutName) : m_layoutName(layoutName) {}
~LayoutType() = default;
/**
* Gets the string type of the layout as defined by that layout in
* Shuffleboard.
*/
wpi::StringRef GetLayoutName() const;
private:
wpi::StringRef m_layoutName;
};
} // namespace frc

View File

@@ -0,0 +1,55 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#pragma once
#include <string>
#include <cscore_c.h>
#include "frc/smartdashboard/SendableBase.h"
namespace cs {
class VideoSource;
} // namespace cs
namespace frc {
/**
* A wrapper to make video sources sendable and usable from Shuffleboard.
*/
class SendableCameraWrapper : public SendableBase {
private:
struct private_init {};
public:
/**
* Creates a new sendable wrapper. Private constructor to avoid direct
* instantiation with multiple wrappers floating around for the same camera.
*
* @param source the source to wrap
*/
SendableCameraWrapper(CS_Source source, const private_init&);
/**
* Gets a sendable wrapper object for the given video source, creating the
* wrapper if one does not already exist for the source.
*
* @param source the video source to wrap
* @return a sendable wrapper object for the video source, usable in
* Shuffleboard via ShuffleboardTab::Add() and ShuffleboardLayout::Add()
*/
static SendableCameraWrapper& Wrap(const cs::VideoSource& source);
static SendableCameraWrapper& Wrap(CS_Source source);
void InitSendable(SendableBuilder& builder) override;
private:
std::string m_uri;
};
} // namespace frc

View File

@@ -18,9 +18,16 @@
#include <wpi/StringMap.h>
#include <wpi/Twine.h>
#include "frc/ErrorBase.h"
#include "frc/WPIErrors.h"
#include "frc/shuffleboard/LayoutType.h"
#include "frc/shuffleboard/ShuffleboardComponentBase.h"
#include "frc/shuffleboard/ShuffleboardValue.h"
namespace cs {
class VideoSource;
} // namespace cs
namespace frc {
class ComplexWidget;
@@ -31,7 +38,8 @@ class SimpleWidget;
/**
* Common interface for objects that can contain shuffleboard components.
*/
class ShuffleboardContainer : public virtual ShuffleboardValue {
class ShuffleboardContainer : public virtual ShuffleboardValue,
public ErrorBase {
public:
explicit ShuffleboardContainer(const wpi::Twine& title);
@@ -49,12 +57,45 @@ class ShuffleboardContainer : public virtual ShuffleboardValue {
* 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 type the type of the layout, eg "List" or "Grid"
* @param title the title of the layout
* @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& type,
const wpi::Twine& title);
ShuffleboardLayout& GetLayout(const wpi::Twine& title,
const LayoutType& 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. Note: this method should
* only be used to use a layout type that is not already built into
* Shuffleboard. To use a layout built into Shuffleboard, use
* {@link #GetLayout(String, LayoutType)} and the layouts in {@link
* BuiltInLayouts}.
*
* @param title the title of the layout
* @param type the type of the layout, eg "List Layout" or "Grid Layout"
* @return the layout
* @see #GetLayout(String, LayoutType)
*/
ShuffleboardLayout& GetLayout(const wpi::Twine& title,
const wpi::Twine& type);
/**
* Gets the already-defined layout in this container with the given title.
*
* <pre>{@code
* Shuffleboard::GetTab("Example Tab")->getLayout("My Layout",
* &BuiltInLayouts.kList);
*
* // Later...
* Shuffleboard::GetTab("Example Tab")->GetLayout("My Layout");
* }</pre>
*
* @param title the title of the layout to get
* @return the layout with the given title
* @throws if no layout has yet been defined with the given title
*/
ShuffleboardLayout& GetLayout(const wpi::Twine& title);
/**
* Adds a widget to this container to display the given sendable.
@@ -67,6 +108,17 @@ class ShuffleboardContainer : public virtual ShuffleboardValue {
*/
ComplexWidget& Add(const wpi::Twine& title, Sendable& sendable);
/**
* Adds a widget to this container to display the given video stream.
*
* @param title the title of the widget
* @param video the video stream to display
* @return a widget to display the sendable data
* @throws IllegalArgumentException if a widget already exists in this
* container with the given title
*/
ComplexWidget& Add(const wpi::Twine& title, const cs::VideoSource& video);
/**
* Adds a widget to this container to display the given sendable.
*
@@ -78,6 +130,16 @@ class ShuffleboardContainer : public virtual ShuffleboardValue {
*/
ComplexWidget& Add(Sendable& sendable);
/**
* Adds a widget to this container to display the given video stream.
*
* @param video the video to display
* @return a widget to display the sendable data
* @throws IllegalArgumentException if a widget already exists in this
* container with the same title as the video source
*/
ComplexWidget& Add(const cs::VideoSource& video);
/**
* Adds a widget to this container to display the given data.
*

View File

@@ -10,6 +10,7 @@
#include <wpi/Twine.h>
#include "frc/shuffleboard/ShuffleboardComponent.h"
#include "frc/shuffleboard/WidgetType.h"
namespace frc {
@@ -36,6 +37,22 @@ class ShuffleboardWidget
*
* @param widgetType the type of the widget used to display the data
* @return this widget object
* @see BuiltInWidgets
*/
Derived& withWidget(const WidgetType& widgetType) {
return WithWidget(widgetType.GetWidgetName());
}
/**
* Sets the type of widget used to display the data. If not set, the default
* widget type will be used. This method should only be used to use a widget
* that does not come built into Shuffleboard (i.e. one that comes with a
* custom or thrid-party plugin). To use a widget that is built into
* Shuffleboard, use {@link #withWidget(WidgetType)} and {@link
* BuiltInWidgets}.
*
* @param widgetType the type of the widget used to display the data
* @return this widget object
*/
Derived& WithWidget(const wpi::Twine& widgetType) {
this->SetType(widgetType);

View File

@@ -0,0 +1,36 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#pragma once
#include <wpi/Twine.h>
namespace frc {
/**
* Represents the type of a widget in Shuffleboard. Using this is preferred over
* specifying raw strings, to avoid typos and having to know or look up the
* exact string name for a desired widget.
*
* @see BuiltInWidgets the built-in widget types
*/
class WidgetType {
public:
explicit WidgetType(const char* widgetName) : m_widgetName(widgetName) {}
~WidgetType() = default;
/**
* Gets the string type of the widget as defined by that widget in
* Shuffleboard.
*/
wpi::StringRef GetWidgetName() const;
private:
wpi::StringRef m_widgetName;
};
} // namespace frc

View File

@@ -20,9 +20,11 @@ namespace frc {
* the Smart Dashboard.
* @deprecated Use Sendable directly instead
*/
class WPI_DEPRECATED("use Sendable directly instead") NamedSendable
: public Sendable {
class NamedSendable : public Sendable {
public:
WPI_DEPRECATED("use Sendable directly instead")
NamedSendable() = default;
void SetName(const wpi::Twine& name) override;
std::string GetSubsystem() const override;
void SetSubsystem(const wpi::Twine& subsystem) override;

View File

@@ -33,7 +33,7 @@ class ShuffleboardInstanceTest : public testing::Test {
TEST_F(ShuffleboardInstanceTest, PathFluent) {
auto entry = m_shuffleboardInstance->GetTab("Tab Title")
.GetLayout("List", "List Layout")
.GetLayout("List Layout", "List")
.Add("Data", "string")
.WithWidget("Text View")
.GetEntry();
@@ -45,10 +45,10 @@ TEST_F(ShuffleboardInstanceTest, PathFluent) {
TEST_F(ShuffleboardInstanceTest, NestedLayoutsFluent) {
auto entry = m_shuffleboardInstance->GetTab("Tab")
.GetLayout("List", "First")
.GetLayout("List", "Second")
.GetLayout("List", "Third")
.GetLayout("List", "Fourth")
.GetLayout("First", "List")
.GetLayout("Second", "List")
.GetLayout("Third", "List")
.GetLayout("Fourth", "List")
.Add("Value", "string")
.GetEntry();
@@ -60,10 +60,10 @@ TEST_F(ShuffleboardInstanceTest, NestedLayoutsFluent) {
TEST_F(ShuffleboardInstanceTest, NestedLayoutsOop) {
ShuffleboardTab& tab = m_shuffleboardInstance->GetTab("Tab");
ShuffleboardLayout& first = tab.GetLayout("List", "First");
ShuffleboardLayout& second = first.GetLayout("List", "Second");
ShuffleboardLayout& third = second.GetLayout("List", "Third");
ShuffleboardLayout& fourth = third.GetLayout("List", "Fourth");
ShuffleboardLayout& first = tab.GetLayout("First", "List");
ShuffleboardLayout& second = first.GetLayout("Second", "List");
ShuffleboardLayout& third = second.GetLayout("Third", "List");
ShuffleboardLayout& fourth = third.GetLayout("Fourth", "List");
SimpleWidget& widget = fourth.Add("Value", "string");
auto entry = widget.GetEntry();
@@ -75,17 +75,17 @@ TEST_F(ShuffleboardInstanceTest, NestedLayoutsOop) {
TEST_F(ShuffleboardInstanceTest, LayoutTypeIsSet) {
std::string layoutType = "Type";
m_shuffleboardInstance->GetTab("Tab").GetLayout(layoutType, "Title");
m_shuffleboardInstance->GetTab("Tab").GetLayout("Title", layoutType);
m_shuffleboardInstance->Update();
nt::NetworkTableEntry entry = m_ntInstance.GetEntry(
"/Shuffleboard/.metadata/Tab/Title/PreferredComponent");
EXPECT_EQ(layoutType, entry.GetString("Not Set")) << "Layout type not set";
}
TEST_F(ShuffleboardInstanceTest, NestedActuatoWidgetsAreDisabled) {
TEST_F(ShuffleboardInstanceTest, NestedActuatorWidgetsAreDisabled) {
MockActuatorSendable sendable("Actuator");
m_shuffleboardInstance->GetTab("Tab")
.GetLayout("Layout", "Title")
.GetLayout("Title", "Type")
.Add(sendable);
auto controllableEntry =
m_ntInstance.GetEntry("/Shuffleboard/Tab/Title/Actuator/.controllable");

View File

@@ -6,7 +6,7 @@
/*----------------------------------------------------------------------------*/
#include <frc/Joystick.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/TimedRobot.h>
#include <frc/drive/DifferentialDrive.h>
@@ -15,8 +15,8 @@
* Runs the motors with arcade steering.
*/
class Robot : public frc::TimedRobot {
frc::Spark m_leftMotor{0};
frc::Spark m_rightMotor{1};
frc::PWMVictorSPX m_leftMotor{0};
frc::PWMVictorSPX m_rightMotor{1};
frc::DifferentialDrive m_robotDrive{m_leftMotor, m_rightMotor};
frc::Joystick m_stick{0};
@@ -27,4 +27,6 @@ class Robot : public frc::TimedRobot {
}
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -60,4 +60,6 @@ class Robot : public frc::TimedRobot {
}
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -37,4 +37,6 @@ class Robot : public frc::TimedRobot {
frc::PowerDistributionPanel m_pdp;
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -80,4 +80,6 @@ class Robot : public frc::TimedRobot {
frc::Encoder m_encoder{1, 2, false, frc::Encoder::k4X};
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -42,4 +42,6 @@ void Robot::TeleopPeriodic() { frc::Scheduler::GetInstance()->Run(); }
void Robot::TestPeriodic() {}
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -8,7 +8,7 @@
#pragma once
#include <frc/DigitalInput.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/commands/Subsystem.h>
/**
@@ -46,6 +46,6 @@ class Claw : public frc::Subsystem {
void Log();
private:
frc::Spark m_motor{7};
frc::PWMVictorSPX m_motor{7};
frc::DigitalInput m_contact{5};
};

View File

@@ -10,7 +10,7 @@
#include <frc/AnalogGyro.h>
#include <frc/AnalogInput.h>
#include <frc/Encoder.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/SpeedControllerGroup.h>
#include <frc/commands/Subsystem.h>
#include <frc/drive/DifferentialDrive.h>
@@ -67,12 +67,12 @@ class DriveTrain : public frc::Subsystem {
double GetDistanceToObstacle();
private:
frc::Spark m_frontLeft{1};
frc::Spark m_rearLeft{2};
frc::PWMVictorSPX m_frontLeft{1};
frc::PWMVictorSPX m_rearLeft{2};
frc::SpeedControllerGroup m_left{m_frontLeft, m_rearLeft};
frc::Spark m_frontRight{3};
frc::Spark m_rearRight{4};
frc::PWMVictorSPX m_frontRight{3};
frc::PWMVictorSPX m_rearRight{4};
frc::SpeedControllerGroup m_right{m_frontRight, m_rearRight};
frc::DifferentialDrive m_robotDrive{m_left, m_right};

View File

@@ -8,7 +8,7 @@
#pragma once
#include <frc/AnalogPotentiometer.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/commands/PIDSubsystem.h>
/**
@@ -42,7 +42,7 @@ class Elevator : public frc::PIDSubsystem {
void UsePIDOutput(double d) override;
private:
frc::Spark m_motor{5};
frc::PWMVictorSPX m_motor{5};
// Conversion value of potentiometer varies between the real world and
// simulation

View File

@@ -8,7 +8,7 @@
#pragma once
#include <frc/AnalogPotentiometer.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/commands/PIDSubsystem.h>
/**
@@ -40,7 +40,7 @@ class Wrist : public frc::PIDSubsystem {
void UsePIDOutput(double d) override;
private:
frc::Spark m_motor{6};
frc::PWMVictorSPX m_motor{6};
// Conversion value of potentiometer varies between the real world and
// simulation

View File

@@ -6,7 +6,7 @@
/*----------------------------------------------------------------------------*/
#include <frc/Joystick.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/TimedRobot.h>
#include <frc/Timer.h>
#include <frc/drive/DifferentialDrive.h>
@@ -46,8 +46,8 @@ class Robot : public frc::TimedRobot {
private:
// Robot drive system
frc::Spark m_left{0};
frc::Spark m_right{1};
frc::PWMVictorSPX m_left{0};
frc::PWMVictorSPX m_right{1};
frc::DifferentialDrive m_robotDrive{m_left, m_right};
frc::Joystick m_stick{0};
@@ -55,4 +55,6 @@ class Robot : public frc::TimedRobot {
frc::Timer m_timer;
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -9,7 +9,7 @@
#include <frc/AnalogGyro.h>
#include <frc/Joystick.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/TimedRobot.h>
#include <frc/drive/DifferentialDrive.h>
@@ -47,12 +47,14 @@ class Robot : public frc::TimedRobot {
static constexpr int kGyroPort = 0;
static constexpr int kJoystickPort = 0;
frc::Spark m_left{kLeftMotorPort};
frc::Spark m_right{kRightMotorPort};
frc::PWMVictorSPX m_left{kLeftMotorPort};
frc::PWMVictorSPX m_right{kRightMotorPort};
frc::DifferentialDrive m_robotDrive{m_left, m_right};
frc::AnalogGyro m_gyro{kGyroPort};
frc::Joystick m_joystick{kJoystickPort};
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -7,7 +7,7 @@
#include <frc/AnalogGyro.h>
#include <frc/Joystick.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/TimedRobot.h>
#include <frc/drive/MecanumDrive.h>
@@ -47,10 +47,10 @@ class Robot : public frc::TimedRobot {
static constexpr int kGyroPort = 0;
static constexpr int kJoystickPort = 0;
frc::Spark m_frontLeft{kFrontLeftMotorPort};
frc::Spark m_rearLeft{kRearLeftMotorPort};
frc::Spark m_frontRight{kFrontRightMotorPort};
frc::Spark m_rearRight{kRearRightMotorPort};
frc::PWMVictorSPX m_frontLeft{kFrontLeftMotorPort};
frc::PWMVictorSPX m_rearLeft{kRearLeftMotorPort};
frc::PWMVictorSPX m_frontRight{kFrontRightMotorPort};
frc::PWMVictorSPX m_rearRight{kRearRightMotorPort};
frc::MecanumDrive m_robotDrive{m_frontLeft, m_rearLeft, m_frontRight,
m_rearRight};
@@ -58,4 +58,6 @@ class Robot : public frc::TimedRobot {
frc::Joystick m_joystick{kJoystickPort};
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -30,4 +30,6 @@ class Robot : public frc::TimedRobot {
frc::XboxController m_hid{0};
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -73,4 +73,6 @@ class Robot : public frc::TimedRobot {
}
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -6,7 +6,7 @@
/*----------------------------------------------------------------------------*/
#include <frc/Joystick.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/TimedRobot.h>
#include <frc/drive/MecanumDrive.h>
@@ -38,14 +38,16 @@ class Robot : public frc::TimedRobot {
static constexpr int kJoystickChannel = 0;
frc::Spark m_frontLeft{kFrontLeftChannel};
frc::Spark m_rearLeft{kRearLeftChannel};
frc::Spark m_frontRight{kFrontRightChannel};
frc::Spark m_rearRight{kRearRightChannel};
frc::PWMVictorSPX m_frontLeft{kFrontLeftChannel};
frc::PWMVictorSPX m_rearLeft{kRearLeftChannel};
frc::PWMVictorSPX m_frontRight{kFrontRightChannel};
frc::PWMVictorSPX m_rearRight{kRearRightChannel};
frc::MecanumDrive m_robotDrive{m_frontLeft, m_rearLeft, m_frontRight,
m_rearRight};
frc::Joystick m_stick{kJoystickChannel};
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -6,7 +6,7 @@
/*----------------------------------------------------------------------------*/
#include <frc/Joystick.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/TimedRobot.h>
/**
@@ -23,7 +23,9 @@ class Robot : public frc::TimedRobot {
private:
frc::Joystick m_stick{0};
frc::Spark m_motor{0};
frc::PWMVictorSPX m_motor{0};
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -7,7 +7,7 @@
#include <frc/Encoder.h>
#include <frc/Joystick.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/TimedRobot.h>
#include <frc/smartdashboard/SmartDashboard.h>
@@ -44,8 +44,10 @@ class Robot : public frc::TimedRobot {
private:
frc::Joystick m_stick{0};
frc::Spark m_motor{0};
frc::PWMVictorSPX m_motor{0};
frc::Encoder m_encoder{0, 1};
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -80,4 +80,6 @@ void Robot::Log() {
drivetrain.GetRightEncoder().GetDistance());
}
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -8,8 +8,8 @@
#pragma once
#include <frc/DigitalInput.h>
#include <frc/PWMVictorSPX.h>
#include <frc/Solenoid.h>
#include <frc/Spark.h>
#include <frc/commands/Subsystem.h>
/**
@@ -69,7 +69,7 @@ class Collector : public frc::Subsystem {
private:
// Subsystem devices
frc::Spark m_rollerMotor{6};
frc::PWMVictorSPX m_rollerMotor{6};
frc::DigitalInput m_ballDetector{10};
frc::Solenoid m_piston{1};
frc::DigitalInput m_openDetector{6};

View File

@@ -9,7 +9,7 @@
#include <frc/AnalogGyro.h>
#include <frc/Encoder.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/SpeedControllerGroup.h>
#include <frc/commands/Subsystem.h>
#include <frc/drive/DifferentialDrive.h>
@@ -63,12 +63,12 @@ class DriveTrain : public frc::Subsystem {
private:
// Subsystem devices
frc::Spark m_frontLeftCIM{1};
frc::Spark m_rearLeftCIM{2};
frc::PWMVictorSPX m_frontLeftCIM{1};
frc::PWMVictorSPX m_rearLeftCIM{2};
frc::SpeedControllerGroup m_leftCIMs{m_frontLeftCIM, m_rearLeftCIM};
frc::Spark m_frontRightCIM{3};
frc::Spark m_rearRightCIM{4};
frc::PWMVictorSPX m_frontRightCIM{3};
frc::PWMVictorSPX m_rearRightCIM{4};
frc::SpeedControllerGroup m_rightCIMs{m_frontRightCIM, m_rearRightCIM};
frc::DifferentialDrive m_robotDrive{m_leftCIMs, m_rightCIMs};

View File

@@ -9,7 +9,7 @@
#include <frc/AnalogPotentiometer.h>
#include <frc/DigitalInput.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/commands/PIDSubsystem.h>
/**
@@ -70,5 +70,5 @@ class Pivot : public frc::PIDSubsystem {
frc::AnalogPotentiometer m_pot{1};
// Motor to move the pivot
frc::Spark m_motor{5};
frc::PWMVictorSPX m_motor{5};
};

View File

@@ -10,7 +10,7 @@
#include <frc/AnalogInput.h>
#include <frc/Joystick.h>
#include <frc/PIDController.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/TimedRobot.h>
/**
@@ -62,7 +62,7 @@ class Robot : public frc::TimedRobot {
frc::AnalogInput m_potentiometer{kPotChannel};
frc::Joystick m_joystick{kJoystickChannel};
frc::Spark m_elevatorMotor{kMotorChannel};
frc::PWMVictorSPX m_elevatorMotor{kMotorChannel};
/* Potentiometer (AnalogInput) and elevatorMotor (Victor) can be used as a
* PIDSource and PIDOutput respectively.
@@ -73,4 +73,6 @@ class Robot : public frc::TimedRobot {
constexpr std::array<double, 3> Robot::kSetPoints;
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -27,4 +27,6 @@ class Robot : public frc::TimedRobot {
}
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -55,4 +55,6 @@ class Robot : public frc::TimedRobot {
static constexpr int kRelayReverseButton = 2;
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -8,7 +8,7 @@
#include <frc/AnalogPotentiometer.h>
#include <frc/Encoder.h>
#include <frc/Joystick.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/TimedRobot.h>
#include <frc/drive/DifferentialDrive.h>
#include <frc/shuffleboard/Shuffleboard.h>
@@ -64,9 +64,9 @@ class Robot : public frc::TimedRobot {
}
private:
frc::Spark m_left{0};
frc::Spark m_right{1};
frc::Spark m_elevatorMotor{2};
frc::PWMVictorSPX m_left{0};
frc::PWMVictorSPX m_right{1};
frc::PWMVictorSPX m_elevatorMotor{2};
frc::DifferentialDrive m_robotDrive{m_left, m_right};
@@ -79,4 +79,6 @@ class Robot : public frc::TimedRobot {
nt::NetworkTableEntry m_maxSpeed;
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -67,4 +67,6 @@ class Robot : public frc::TimedRobot {
static constexpr int kDoubleSolenoidReverse = 3;
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -6,7 +6,7 @@
/*----------------------------------------------------------------------------*/
#include <frc/AnalogInput.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/TimedRobot.h>
#include <frc/drive/DifferentialDrive.h>
@@ -45,9 +45,11 @@ class Robot : public frc::TimedRobot {
frc::AnalogInput m_ultrasonic{kUltrasonicPort};
frc::Spark m_left{kLeftMotorPort};
frc::Spark m_right{kRightMotorPort};
frc::PWMVictorSPX m_left{kLeftMotorPort};
frc::PWMVictorSPX m_right{kRightMotorPort};
frc::DifferentialDrive m_robotDrive{m_left, m_right};
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -8,7 +8,7 @@
#include <frc/AnalogInput.h>
#include <frc/PIDController.h>
#include <frc/PIDOutput.h>
#include <frc/Spark.h>
#include <frc/PWMVictorSPX.h>
#include <frc/TimedRobot.h>
#include <frc/drive/DifferentialDrive.h>
@@ -72,12 +72,14 @@ class Robot : public frc::TimedRobot {
frc::AnalogInput m_ultrasonic{kUltrasonicPort};
frc::Spark m_left{kLeftMotorPort};
frc::Spark m_right{kRightMotorPort};
frc::PWMVictorSPX m_left{kLeftMotorPort};
frc::PWMVictorSPX m_right{kRightMotorPort};
frc::DifferentialDrive m_robotDrive{m_left, m_right};
MyPIDOutput m_pidOutput{m_robotDrive};
frc::PIDController m_pidController{kP, kI, kD, m_ultrasonic, m_pidOutput};
};
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

View File

@@ -10,8 +10,8 @@
#include <string>
#include <frc/Joystick.h>
#include <frc/PWMVictorSPX.h>
#include <frc/SampleRobot.h>
#include <frc/Spark.h>
#include <frc/drive/DifferentialDrive.h>
#include <frc/smartdashboard/SendableChooser.h>
@@ -37,8 +37,8 @@ class Robot : public frc::SampleRobot {
private:
// Robot drive system
frc::Spark m_leftMotor{0};
frc::Spark m_rightMotor{1};
frc::PWMVictorSPX m_leftMotor{0};
frc::PWMVictorSPX m_rightMotor{1};
frc::DifferentialDrive m_robotDrive{m_leftMotor, m_rightMotor};
frc::Joystick m_stick{0};

View File

@@ -7,6 +7,10 @@
#include "frc/PowerDistributionPanel.h" // NOLINT(build/include_order)
#include <thread>
#include <hal/Ports.h>
#include "TestBench.h"
#include "frc/Jaguar.h"
#include "frc/Talon.h"
@@ -40,6 +44,20 @@ class PowerDistributionPanelTest : public testing::Test {
}
};
TEST_F(PowerDistributionPanelTest, CheckRepeatedCalls) {
auto numChannels = HAL_GetNumPDPChannels();
// 1 second
for (int i = 0; i < 50; i++) {
for (int j = 0; j < numChannels; j++) {
m_pdp->GetCurrent(j);
ASSERT_TRUE(m_pdp->GetError().GetCode() == 0);
}
m_pdp->GetVoltage();
ASSERT_TRUE(m_pdp->GetError().GetCode() == 0);
}
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
/**
* Test if the current changes when the motor is driven using a talon
*/

View File

@@ -39,16 +39,16 @@ TEST_F(ShuffleboardInstanceTest, PathFluent) {
.GetEntry();
EXPECT_EQ("string", entry.GetString("")) << "Wrong entry value";
EXPECT_EQ("/Shuffleboard/Tab Title/List Layout/Data", entry.GetName())
EXPECT_EQ("/Shuffleboard/Tab Title/List/Data", entry.GetName())
<< "Entry path generated incorrectly";
}
TEST_F(ShuffleboardInstanceTest, NestedLayoutsFluent) {
auto entry = m_shuffleboardInstance->GetTab("Tab")
.GetLayout("List", "First")
.GetLayout("List", "Second")
.GetLayout("List", "Third")
.GetLayout("List", "Fourth")
.GetLayout("First", "List")
.GetLayout("Second", "List")
.GetLayout("Third", "List")
.GetLayout("Fourth", "List")
.Add("Value", "string")
.GetEntry();
@@ -60,10 +60,10 @@ TEST_F(ShuffleboardInstanceTest, NestedLayoutsFluent) {
TEST_F(ShuffleboardInstanceTest, NestedLayoutsOop) {
ShuffleboardTab& tab = m_shuffleboardInstance->GetTab("Tab");
ShuffleboardLayout& first = tab.GetLayout("List", "First");
ShuffleboardLayout& second = first.GetLayout("List", "Second");
ShuffleboardLayout& third = second.GetLayout("List", "Third");
ShuffleboardLayout& fourth = third.GetLayout("List", "Fourth");
ShuffleboardLayout& first = tab.GetLayout("First", "List");
ShuffleboardLayout& second = first.GetLayout("Second", "List");
ShuffleboardLayout& third = second.GetLayout("Third", "List");
ShuffleboardLayout& fourth = third.GetLayout("Fourth", "List");
SimpleWidget& widget = fourth.Add("Value", "string");
auto entry = widget.GetEntry();
@@ -75,17 +75,17 @@ TEST_F(ShuffleboardInstanceTest, NestedLayoutsOop) {
TEST_F(ShuffleboardInstanceTest, LayoutTypeIsSet) {
std::string layoutType = "Type";
m_shuffleboardInstance->GetTab("Tab").GetLayout(layoutType, "Title");
m_shuffleboardInstance->GetTab("Tab").GetLayout("Title", layoutType);
m_shuffleboardInstance->Update();
nt::NetworkTableEntry entry = m_ntInstance.GetEntry(
"/Shuffleboard/.metadata/Tab/Title/PreferredComponent");
EXPECT_EQ(layoutType, entry.GetString("Not Set")) << "Layout type not set";
}
TEST_F(ShuffleboardInstanceTest, NestedActuatoWidgetsAreDisabled) {
TEST_F(ShuffleboardInstanceTest, NestedActuatorWidgetsAreDisabled) {
MockActuatorSendable sendable("Actuator");
m_shuffleboardInstance->GetTab("Tab")
.GetLayout("Layout", "Title")
.GetLayout("Title", "Layout")
.Add(sendable);
auto controllableEntry =
m_ntInstance.GetEntry("/Shuffleboard/Tab/Title/Actuator/.controllable");

View File

@@ -129,7 +129,7 @@ public final class CameraServer {
}
}
return values.toArray(String[]::new);
return values.toArray(new String[0]);
}
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP"})

View File

@@ -1116,10 +1116,20 @@ public class DriverStation {
* Provides the service routine for the DS polling m_thread.
*/
private void run() {
int safetyCounter = 0;
while (m_threadKeepAlive) {
HAL.waitForDSData();
getData();
if (isDisabled()) {
safetyCounter = 0;
}
safetyCounter++;
if (safetyCounter >= 4) {
MotorSafety.checkMotors();
safetyCounter = 0;
}
if (m_userInDisabled) {
HAL.observeUserProgramDisabled();
}

View File

@@ -26,7 +26,8 @@ import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
*
* <p>init() functions -- each of the following functions is called once when the
* appropriate mode is entered:
* - disabledInit() -- called only when first disabled
* - disabledInit() -- called each and every time disabled is entered from
* another mode
* - autonomousInit() -- called each and every time autonomous is entered from
* another mode
* - teleopInit() -- called each and every time teleop is entered from

View File

@@ -7,6 +7,9 @@
package edu.wpi.first.wpilibj;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* This base class runs a watchdog timer and calls the subclass's StopMotor()
* function if the timeout expires.
@@ -16,9 +19,21 @@ package edu.wpi.first.wpilibj;
public abstract class MotorSafety {
private static final double kDefaultSafetyExpiration = 0.1;
private final Watchdog m_watchdog = new Watchdog(kDefaultSafetyExpiration, this::timeoutFunc);
private double m_expiration = kDefaultSafetyExpiration;
private boolean m_enabled;
private double m_stopTime = Timer.getFPGATimestamp();
private final Object m_thisMutex = new Object();
private static final Set<MotorSafety> m_instanceList = new LinkedHashSet<>();
private static final Object m_listMutex = new Object();
/**
* MotorSafety constructor.
*/
public MotorSafety() {
synchronized (m_listMutex) {
m_instanceList.add(this);
}
}
/**
* Feed the motor safety object.
@@ -27,7 +42,7 @@ public abstract class MotorSafety {
*/
public void feed() {
synchronized (m_thisMutex) {
m_watchdog.reset();
m_stopTime = Timer.getFPGATimestamp() + m_expiration;
}
}
@@ -38,7 +53,7 @@ public abstract class MotorSafety {
*/
public void setExpiration(double expirationTime) {
synchronized (m_thisMutex) {
m_watchdog.setTimeout(expirationTime);
m_expiration = expirationTime;
}
}
@@ -49,7 +64,7 @@ public abstract class MotorSafety {
*/
public double getExpiration() {
synchronized (m_thisMutex) {
return m_watchdog.getTimeout();
return m_expiration;
}
}
@@ -60,7 +75,32 @@ public abstract class MotorSafety {
*/
public boolean isAlive() {
synchronized (m_thisMutex) {
return !m_enabled || !m_watchdog.isExpired();
return !m_enabled || m_stopTime > Timer.getFPGATimestamp();
}
}
/**
* Check if this motor has exceeded its timeout. This method is called periodically to determine
* if this motor has exceeded its timeout value. If it has, the stop method is called, and the
* motor is shut down until its value is updated again.
*/
public void check() {
boolean enabled;
double stopTime;
synchronized (m_thisMutex) {
enabled = m_enabled;
stopTime = m_stopTime;
}
if (!enabled || RobotState.isDisabled() || RobotState.isTest()) {
return;
}
if (stopTime < Timer.getFPGATimestamp()) {
DriverStation.reportError(getDescription() + "... Output not updated often enough.", false);
stopMotor();
}
}
@@ -73,11 +113,6 @@ public abstract class MotorSafety {
*/
public void setSafetyEnabled(boolean enabled) {
synchronized (m_thisMutex) {
if (enabled) {
m_watchdog.enable();
} else {
m_watchdog.disable();
}
m_enabled = enabled;
}
}
@@ -95,15 +130,18 @@ public abstract class MotorSafety {
}
}
private void timeoutFunc() {
DriverStation ds = DriverStation.getInstance();
if (ds.isDisabled() || ds.isTest()) {
return;
/**
* Check the motors to see if any have timed out.
*
* <p>This static method is called periodically to poll all the motors and stop any that have
* timed out.
*/
public static void checkMotors() {
synchronized (m_listMutex) {
for (MotorSafety elem : m_instanceList) {
elem.check();
}
}
DriverStation.reportError(getDescription() + "... Output not updated often enough.", false);
stopMotor();
}
public abstract void stopMotor();

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