mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-24 01:31:46 +00:00
Compare commits
38 Commits
v2019.1.1-
...
v2019.2.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e97e7a7611 | ||
|
|
308bdbe298 | ||
|
|
f889b45d59 | ||
|
|
444b899a9f | ||
|
|
f121ccff0d | ||
|
|
bc2c932f92 | ||
|
|
6bdd7ce506 | ||
|
|
c12d7729e3 | ||
|
|
3635116049 | ||
|
|
6105873cbe | ||
|
|
80f87ff8ad | ||
|
|
a2368a6199 | ||
|
|
ae3cb6c83b | ||
|
|
f0f196e5b3 | ||
|
|
7c35355d29 | ||
|
|
75cc09a9e4 | ||
|
|
0e2e180635 | ||
|
|
01d1322066 | ||
|
|
ceed1d74dc | ||
|
|
e1bf623997 | ||
|
|
d46ce13ffe | ||
|
|
300eeb330d | ||
|
|
d817001259 | ||
|
|
8ac4b113a5 | ||
|
|
f3864e9abb | ||
|
|
799c3ea8a6 | ||
|
|
8d95c38e39 | ||
|
|
a7f4e29b73 | ||
|
|
b88369f5e8 | ||
|
|
ce6f1d0588 | ||
|
|
f163216a4c | ||
|
|
c449ef1064 | ||
|
|
6593f4346e | ||
|
|
ce1367a115 | ||
|
|
0d7d880261 | ||
|
|
ca2acec88c | ||
|
|
3721463eb3 | ||
|
|
6e8f8be370 |
@@ -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'
|
||||
|
||||
@@ -123,7 +123,7 @@ public final class CameraServer {
|
||||
}
|
||||
}
|
||||
|
||||
return values.toArray(String[]::new);
|
||||
return values.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP"})
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>{};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -82,3 +82,4 @@ kResourceType_DigilentDMC60 = 80
|
||||
kResourceType_PWMVictorSPX = 81
|
||||
kResourceType_RevSparkMaxPWM = 82
|
||||
kResourceType_RevSparkMaxCAN = 83
|
||||
kResourceType_ADIS16470 = 84
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,18 +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,
|
||||
@@ -217,16 +197,14 @@ 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 {
|
||||
@@ -256,25 +234,22 @@ 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;
|
||||
@@ -304,15 +279,14 @@ 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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -322,25 +296,22 @@ 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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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()};
|
||||
|
||||
@@ -103,6 +103,8 @@ class DriverStationSim {
|
||||
HALSIM_SetDriverStationDsAttached(dsAttached);
|
||||
}
|
||||
|
||||
void NotifyNewData() { HALSIM_NotifyDriverStationNewData(); }
|
||||
|
||||
void ResetData() { HALSIM_ResetDriverStationData(); }
|
||||
};
|
||||
} // namespace sim
|
||||
|
||||
@@ -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,18 +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,
|
||||
@@ -211,16 +206,14 @@ 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 {
|
||||
@@ -250,25 +243,22 @@ 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;
|
||||
@@ -298,10 +288,9 @@ 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) {
|
||||
uint32_t now = GetPacketBaseTime();
|
||||
if (now - i->second.lastTimeStamp < static_cast<uint32_t>(periodMs)) {
|
||||
*status = 0;
|
||||
// Read the data from the stored message into the output
|
||||
std::memcpy(data, i->second.data, i->second.length);
|
||||
@@ -316,25 +305,22 @@ 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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -100,9 +100,8 @@ sourceSets {
|
||||
dev
|
||||
}
|
||||
|
||||
compileJava {
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.compilerArgs = ['--release', '8']
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -10,55 +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(); });
|
||||
m_watchdog.SuppressTimeoutMessage(true);
|
||||
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_watchdog.SuppressTimeoutMessage(true);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -67,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -58,10 +58,15 @@ void Watchdog::Thread::Main() {
|
||||
<< "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
|
||||
|
||||
13
wpilibc/src/main/native/cpp/shuffleboard/BuiltInLayouts.cpp
Normal file
13
wpilibc/src/main/native/cpp/shuffleboard/BuiltInLayouts.cpp
Normal 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"};
|
||||
35
wpilibc/src/main/native/cpp/shuffleboard/BuiltInWidgets.cpp
Normal file
35
wpilibc/src/main/native/cpp/shuffleboard/BuiltInWidgets.cpp
Normal 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"};
|
||||
12
wpilibc/src/main/native/cpp/shuffleboard/LayoutType.cpp
Normal file
12
wpilibc/src/main/native/cpp/shuffleboard/LayoutType.cpp
Normal 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; }
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
12
wpilibc/src/main/native/cpp/shuffleboard/WidgetType.cpp
Normal file
12
wpilibc/src/main/native/cpp/shuffleboard/WidgetType.cpp
Normal 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; }
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -60,4 +60,6 @@ class Robot : public frc::TimedRobot {
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef RUNNING_FRC_TESTS
|
||||
int main() { return frc::StartRobot<Robot>(); }
|
||||
#endif
|
||||
|
||||
@@ -37,4 +37,6 @@ class Robot : public frc::TimedRobot {
|
||||
frc::PowerDistributionPanel m_pdp;
|
||||
};
|
||||
|
||||
#ifndef RUNNING_FRC_TESTS
|
||||
int main() { return frc::StartRobot<Robot>(); }
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
};
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -73,4 +73,6 @@ class Robot : public frc::TimedRobot {
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef RUNNING_FRC_TESTS
|
||||
int main() { return frc::StartRobot<Robot>(); }
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -80,4 +80,6 @@ void Robot::Log() {
|
||||
drivetrain.GetRightEncoder().GetDistance());
|
||||
}
|
||||
|
||||
#ifndef RUNNING_FRC_TESTS
|
||||
int main() { return frc::StartRobot<Robot>(); }
|
||||
#endif
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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};
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,4 +27,6 @@ class Robot : public frc::TimedRobot {
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef RUNNING_FRC_TESTS
|
||||
int main() { return frc::StartRobot<Robot>(); }
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -129,7 +129,7 @@ public final class CameraServer {
|
||||
}
|
||||
}
|
||||
|
||||
return values.toArray(String[]::new);
|
||||
return values.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP"})
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,12 +19,20 @@ 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() {
|
||||
m_watchdog.suppressTimeoutMessage(true);
|
||||
synchronized (m_listMutex) {
|
||||
m_instanceList.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,7 +42,7 @@ public abstract class MotorSafety {
|
||||
*/
|
||||
public void feed() {
|
||||
synchronized (m_thisMutex) {
|
||||
m_watchdog.reset();
|
||||
m_stopTime = Timer.getFPGATimestamp() + m_expiration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +53,7 @@ public abstract class MotorSafety {
|
||||
*/
|
||||
public void setExpiration(double expirationTime) {
|
||||
synchronized (m_thisMutex) {
|
||||
m_watchdog.setTimeout(expirationTime);
|
||||
m_expiration = expirationTime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +64,7 @@ public abstract class MotorSafety {
|
||||
*/
|
||||
public double getExpiration() {
|
||||
synchronized (m_thisMutex) {
|
||||
return m_watchdog.getTimeout();
|
||||
return m_expiration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,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;
|
||||
}
|
||||
}
|
||||
@@ -99,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();
|
||||
|
||||
@@ -237,23 +237,25 @@ public abstract class RobotBase implements AutoCloseable {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final File file = new File("/tmp/frc_versions/FRC_Lib_Version.ini");
|
||||
if (isReal()) {
|
||||
try {
|
||||
final File file = new File("/tmp/frc_versions/FRC_Lib_Version.ini");
|
||||
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
|
||||
file.createNewFile();
|
||||
|
||||
try (OutputStream output = Files.newOutputStream(file.toPath())) {
|
||||
output.write("Java ".getBytes(StandardCharsets.UTF_8));
|
||||
output.write(WPILibVersion.Version.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
} catch (IOException ex) {
|
||||
DriverStation.reportError("Could not write FRC_Lib_Version.ini: " + ex.toString(),
|
||||
ex.getStackTrace());
|
||||
}
|
||||
|
||||
file.createNewFile();
|
||||
|
||||
try (OutputStream output = Files.newOutputStream(file.toPath())) {
|
||||
output.write("Java ".getBytes(StandardCharsets.UTF_8));
|
||||
output.write(WPILibVersion.Version.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
} catch (IOException ex) {
|
||||
DriverStation.reportError("Could not write FRC_Lib_Version.ini: " + ex.toString(),
|
||||
ex.getStackTrace());
|
||||
}
|
||||
|
||||
boolean errorOnExit = false;
|
||||
|
||||
@@ -70,7 +70,7 @@ public class Timer {
|
||||
*/
|
||||
public synchronized double get() {
|
||||
if (m_running) {
|
||||
return ((getMsClock() - m_startTime) + m_accumulatedTime) / 1000.0;
|
||||
return m_accumulatedTime + (getMsClock() - m_startTime) / 1000.0;
|
||||
} else {
|
||||
return m_accumulatedTime;
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ public class Watchdog implements Closeable, Comparable<Watchdog> {
|
||||
if (now - m_lastEpochsPrintTime > kMinPrintPeriod) {
|
||||
m_lastEpochsPrintTime = now;
|
||||
m_epochs.forEach((key, value) -> {
|
||||
System.out.format("\t" + key + ": %.6fs\n", value / 1.0e6);
|
||||
System.out.format("\t%s: %.6fs\n", key, value / 1.0e6);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -247,10 +247,15 @@ public class Watchdog implements Closeable, Comparable<Watchdog> {
|
||||
System.out.format("Watchdog not fed within %.6fs\n", watchdog.m_timeout / 1.0e6);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
m_queueMutex.unlock();
|
||||
watchdog.m_callback.run();
|
||||
m_queueMutex.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
|
||||
@@ -276,9 +281,7 @@ public class Watchdog implements Closeable, Comparable<Watchdog> {
|
||||
private static boolean awaitUntil(Condition cond, long time) {
|
||||
long delta = time - RobotController.getFPGATime();
|
||||
try {
|
||||
if (delta > 0) {
|
||||
return cond.await(delta, TimeUnit.MICROSECONDS);
|
||||
}
|
||||
return cond.await(delta, TimeUnit.MICROSECONDS);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
ex.printStackTrace();
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* The types of layouts bundled with Shuffleboard.
|
||||
*
|
||||
* <pre>{@code
|
||||
* ShuffleboardLayout myList = Shuffleboard.getTab("My Tab")
|
||||
* .getLayout(BuiltinLayouts.kList, "My List");
|
||||
* }</pre>
|
||||
*/
|
||||
public enum BuiltInLayouts implements LayoutType {
|
||||
/**
|
||||
* 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>
|
||||
*/
|
||||
kList("List Layout"),
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*/
|
||||
kGrid("Grid Layout"),
|
||||
;
|
||||
|
||||
private final String m_layoutName;
|
||||
|
||||
BuiltInLayouts(String layoutName) {
|
||||
m_layoutName = layoutName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLayoutName() {
|
||||
return m_layoutName;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user