mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
[wpilib] Remove Shuffleboard API (#7730)
This commit is contained in:
@@ -14,7 +14,6 @@
|
||||
#include "frc/DSControlWord.h"
|
||||
#include "frc/Errors.h"
|
||||
#include "frc/livewindow/LiveWindow.h"
|
||||
#include "frc/shuffleboard/Shuffleboard.h"
|
||||
#include "frc/smartdashboard/SmartDashboard.h"
|
||||
|
||||
using namespace frc;
|
||||
@@ -153,7 +152,6 @@ void IterativeRobotBase::LoopFunc() {
|
||||
} else if (m_lastMode == Mode::kTest) {
|
||||
if (m_lwEnabledInTest) {
|
||||
LiveWindow::SetEnabled(false);
|
||||
Shuffleboard::DisableActuatorWidgets();
|
||||
}
|
||||
TestExit();
|
||||
}
|
||||
@@ -171,7 +169,6 @@ void IterativeRobotBase::LoopFunc() {
|
||||
} else if (mode == Mode::kTest) {
|
||||
if (m_lwEnabledInTest) {
|
||||
LiveWindow::SetEnabled(true);
|
||||
Shuffleboard::EnableActuatorWidgets();
|
||||
}
|
||||
TestInit();
|
||||
m_watchdog.AddEpoch("TestInit()");
|
||||
@@ -206,8 +203,6 @@ void IterativeRobotBase::LoopFunc() {
|
||||
m_watchdog.AddEpoch("SmartDashboard::UpdateValues()");
|
||||
LiveWindow::UpdateValues();
|
||||
m_watchdog.AddEpoch("LiveWindow::UpdateValues()");
|
||||
Shuffleboard::Update();
|
||||
m_watchdog.AddEpoch("Shuffleboard::Update()");
|
||||
|
||||
if constexpr (IsSimulation()) {
|
||||
HAL_SimPeriodicBefore();
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/ComplexWidget.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <wpi/sendable/Sendable.h>
|
||||
|
||||
#include "frc/smartdashboard/SendableBuilderImpl.h"
|
||||
|
||||
using namespace frc;
|
||||
|
||||
ComplexWidget::ComplexWidget(ShuffleboardContainer& parent,
|
||||
std::string_view title, wpi::Sendable& sendable)
|
||||
: ShuffleboardValue(title),
|
||||
ShuffleboardWidget(parent, title),
|
||||
m_sendable(sendable) {}
|
||||
|
||||
ComplexWidget::~ComplexWidget() = default;
|
||||
|
||||
void ComplexWidget::EnableIfActuator() {
|
||||
if (m_builder && static_cast<SendableBuilderImpl&>(*m_builder).IsActuator()) {
|
||||
static_cast<SendableBuilderImpl&>(*m_builder).StartLiveWindowMode();
|
||||
}
|
||||
}
|
||||
|
||||
void ComplexWidget::DisableIfActuator() {
|
||||
if (m_builder && static_cast<SendableBuilderImpl&>(*m_builder).IsActuator()) {
|
||||
static_cast<SendableBuilderImpl&>(*m_builder).StopLiveWindowMode();
|
||||
}
|
||||
}
|
||||
|
||||
void ComplexWidget::BuildInto(std::shared_ptr<nt::NetworkTable> parentTable,
|
||||
std::shared_ptr<nt::NetworkTable> metaTable) {
|
||||
BuildMetadata(metaTable);
|
||||
if (!m_builder) {
|
||||
m_builder = std::make_unique<SendableBuilderImpl>();
|
||||
static_cast<SendableBuilderImpl&>(*m_builder)
|
||||
.SetTable(parentTable->GetSubTable(GetTitle()));
|
||||
m_sendable.InitSendable(static_cast<SendableBuilderImpl&>(*m_builder));
|
||||
static_cast<SendableBuilderImpl&>(*m_builder).StartListeners();
|
||||
}
|
||||
m_builder->Update();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/LayoutType.h"
|
||||
|
||||
using namespace frc;
|
||||
|
||||
std::string_view LayoutType::GetLayoutName() const {
|
||||
return m_layoutName;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/RecordingController.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "frc/Errors.h"
|
||||
|
||||
using namespace frc;
|
||||
using namespace frc::detail;
|
||||
|
||||
RecordingController::RecordingController(nt::NetworkTableInstance ntInstance) {
|
||||
m_recordingControlEntry =
|
||||
ntInstance.GetBooleanTopic("/Shuffleboard/.recording/RecordData")
|
||||
.Publish();
|
||||
m_recordingFileNameFormatEntry =
|
||||
ntInstance.GetStringTopic("/Shuffleboard/.recording/FileNameFormat")
|
||||
.Publish();
|
||||
m_eventsTable = ntInstance.GetTable("/Shuffleboard/.recording/events");
|
||||
}
|
||||
|
||||
void RecordingController::StartRecording() {
|
||||
m_recordingControlEntry.Set(true);
|
||||
}
|
||||
|
||||
void RecordingController::StopRecording() {
|
||||
m_recordingControlEntry.Set(false);
|
||||
}
|
||||
|
||||
void RecordingController::SetRecordingFileNameFormat(std::string_view format) {
|
||||
m_recordingFileNameFormatEntry.Set(format);
|
||||
}
|
||||
|
||||
void RecordingController::ClearRecordingFileNameFormat() {
|
||||
m_recordingFileNameFormatEntry.Set("");
|
||||
}
|
||||
|
||||
void RecordingController::AddEventMarker(
|
||||
std::string_view name, std::string_view description,
|
||||
ShuffleboardEventImportance importance) {
|
||||
if (name.empty()) {
|
||||
FRC_ReportError(err::Error, "Shuffleboard event name was not specified");
|
||||
return;
|
||||
}
|
||||
m_eventsTable->GetSubTable(name)->GetEntry("Info").SetStringArray(
|
||||
{{std::string{description},
|
||||
std::string{ShuffleboardEventImportanceName(importance)}}});
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/SendableCameraWrapper.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <wpi/StringMap.h>
|
||||
#include <wpi/sendable/SendableBuilder.h>
|
||||
#include <wpi/sendable/SendableRegistry.h>
|
||||
|
||||
namespace frc {
|
||||
namespace detail {
|
||||
std::shared_ptr<SendableCameraWrapper>& GetSendableCameraWrapper(
|
||||
std::string_view cameraName) {
|
||||
static wpi::StringMap<std::shared_ptr<SendableCameraWrapper>> wrappers;
|
||||
return wrappers[cameraName];
|
||||
}
|
||||
|
||||
void AddToSendableRegistry(wpi::Sendable* sendable, std::string_view name) {
|
||||
wpi::SendableRegistry::Add(sendable, name);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
void SendableCameraWrapper::InitSendable(wpi::SendableBuilder& builder) {
|
||||
builder.AddStringProperty(
|
||||
".ShuffleboardURI", [this] { return m_uri; }, nullptr);
|
||||
}
|
||||
} // namespace frc
|
||||
@@ -1,92 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/Shuffleboard.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardTab.h"
|
||||
|
||||
using namespace frc;
|
||||
|
||||
void Shuffleboard::Update() {
|
||||
GetInstance().Update();
|
||||
}
|
||||
|
||||
ShuffleboardTab& Shuffleboard::GetTab(std::string_view title) {
|
||||
return GetInstance().GetTab(title);
|
||||
}
|
||||
|
||||
void Shuffleboard::SelectTab(int index) {
|
||||
GetInstance().SelectTab(index);
|
||||
}
|
||||
|
||||
void Shuffleboard::SelectTab(std::string_view title) {
|
||||
GetInstance().SelectTab(title);
|
||||
}
|
||||
|
||||
void Shuffleboard::EnableActuatorWidgets() {
|
||||
GetInstance().EnableActuatorWidgets();
|
||||
}
|
||||
|
||||
void Shuffleboard::DisableActuatorWidgets() {
|
||||
// Need to update to make sure the sendable builders are initialized
|
||||
Update();
|
||||
GetInstance().DisableActuatorWidgets();
|
||||
}
|
||||
|
||||
void Shuffleboard::StartRecording() {
|
||||
GetRecordingController().StartRecording();
|
||||
}
|
||||
|
||||
void Shuffleboard::StopRecording() {
|
||||
GetRecordingController().StopRecording();
|
||||
}
|
||||
|
||||
void Shuffleboard::SetRecordingFileNameFormat(std::string_view format) {
|
||||
GetRecordingController().SetRecordingFileNameFormat(format);
|
||||
}
|
||||
|
||||
void Shuffleboard::ClearRecordingFileNameFormat() {
|
||||
GetRecordingController().ClearRecordingFileNameFormat();
|
||||
}
|
||||
|
||||
void Shuffleboard::AddEventMarker(std::string_view name,
|
||||
std::string_view description,
|
||||
ShuffleboardEventImportance importance) {
|
||||
GetRecordingController().AddEventMarker(name, description, importance);
|
||||
}
|
||||
|
||||
void Shuffleboard::AddEventMarker(std::string_view name,
|
||||
ShuffleboardEventImportance importance) {
|
||||
AddEventMarker(name, "", importance);
|
||||
}
|
||||
|
||||
static std::unique_ptr<detail::ShuffleboardInstance>& GetInstanceHolder() {
|
||||
static std::unique_ptr<detail::ShuffleboardInstance> instance =
|
||||
std::make_unique<detail::ShuffleboardInstance>(
|
||||
nt::NetworkTableInstance::GetDefault());
|
||||
return instance;
|
||||
}
|
||||
|
||||
#ifndef __FRC_ROBORIO__
|
||||
namespace frc::impl {
|
||||
void ResetShuffleboardInstance() {
|
||||
GetInstanceHolder() = std::make_unique<detail::ShuffleboardInstance>(
|
||||
nt::NetworkTableInstance::GetDefault());
|
||||
}
|
||||
} // namespace frc::impl
|
||||
#endif
|
||||
|
||||
detail::ShuffleboardInstance& Shuffleboard::GetInstance() {
|
||||
return *GetInstanceHolder();
|
||||
}
|
||||
|
||||
detail::RecordingController& Shuffleboard::GetRecordingController() {
|
||||
static detail::RecordingController inst(
|
||||
nt::NetworkTableInstance::GetDefault());
|
||||
return inst;
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardComponentBase.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
using namespace frc;
|
||||
|
||||
ShuffleboardComponentBase::ShuffleboardComponentBase(
|
||||
ShuffleboardContainer& parent, std::string_view title,
|
||||
std::string_view type)
|
||||
: ShuffleboardValue(title), m_parent(parent), m_type(type) {}
|
||||
|
||||
void ShuffleboardComponentBase::SetType(std::string_view type) {
|
||||
m_type = type;
|
||||
m_metadataDirty = true;
|
||||
}
|
||||
|
||||
void ShuffleboardComponentBase::BuildMetadata(
|
||||
std::shared_ptr<nt::NetworkTable> metaTable) {
|
||||
if (!m_metadataDirty) {
|
||||
return;
|
||||
}
|
||||
// Component type
|
||||
if (!GetType().empty()) {
|
||||
metaTable->GetEntry("PreferredComponent").SetString(GetType());
|
||||
}
|
||||
|
||||
// Tile size
|
||||
if (m_width > 0 && m_height > 0) {
|
||||
metaTable->GetEntry("Size").SetDoubleArray(
|
||||
{{static_cast<double>(m_width), static_cast<double>(m_height)}});
|
||||
}
|
||||
|
||||
// Tile position
|
||||
if (m_column >= 0 && m_row >= 0) {
|
||||
metaTable->GetEntry("Position")
|
||||
.SetDoubleArray(
|
||||
{{static_cast<double>(m_column), static_cast<double>(m_row)}});
|
||||
}
|
||||
|
||||
// Custom properties
|
||||
if (GetProperties().size() > 0) {
|
||||
auto propTable = metaTable->GetSubTable("Properties");
|
||||
for (auto& entry : GetProperties()) {
|
||||
propTable->GetEntry(entry.first).SetValue(entry.second);
|
||||
}
|
||||
}
|
||||
m_metadataDirty = false;
|
||||
}
|
||||
|
||||
ShuffleboardContainer& ShuffleboardComponentBase::GetParent() {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
const std::string& ShuffleboardComponentBase::GetType() const {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
const wpi::StringMap<nt::Value>& ShuffleboardComponentBase::GetProperties()
|
||||
const {
|
||||
return m_properties;
|
||||
}
|
||||
@@ -1,404 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardContainer.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <ntcore_cpp.h>
|
||||
#include <wpi/sendable/SendableRegistry.h>
|
||||
|
||||
#include "frc/Errors.h"
|
||||
#include "frc/shuffleboard/ComplexWidget.h"
|
||||
#include "frc/shuffleboard/ShuffleboardComponent.h"
|
||||
#include "frc/shuffleboard/ShuffleboardLayout.h"
|
||||
#include "frc/shuffleboard/SimpleWidget.h"
|
||||
|
||||
using namespace frc;
|
||||
|
||||
static constexpr const char* layoutStrings[] = {"List Layout", "Grid Layout"};
|
||||
|
||||
static constexpr const char* GetStringFromBuiltInLayout(BuiltInLayouts layout) {
|
||||
return layoutStrings[static_cast<int>(layout)];
|
||||
}
|
||||
|
||||
ShuffleboardContainer::ShuffleboardContainer(std::string_view title)
|
||||
: ShuffleboardValue(title) {}
|
||||
|
||||
const std::vector<std::unique_ptr<ShuffleboardComponentBase>>&
|
||||
ShuffleboardContainer::GetComponents() const {
|
||||
return m_components;
|
||||
}
|
||||
|
||||
ShuffleboardLayout& ShuffleboardContainer::GetLayout(std::string_view title,
|
||||
BuiltInLayouts type) {
|
||||
return GetLayout(title, GetStringFromBuiltInLayout(type));
|
||||
}
|
||||
|
||||
ShuffleboardLayout& ShuffleboardContainer::GetLayout(std::string_view title,
|
||||
const LayoutType& type) {
|
||||
return GetLayout(title, type.GetLayoutName());
|
||||
}
|
||||
|
||||
ShuffleboardLayout& ShuffleboardContainer::GetLayout(std::string_view title,
|
||||
std::string_view type) {
|
||||
if (m_layouts.count(title) == 0) {
|
||||
auto layout = std::make_unique<ShuffleboardLayout>(*this, title, type);
|
||||
auto ptr = layout.get();
|
||||
m_components.emplace_back(std::move(layout));
|
||||
m_layouts.insert(std::pair{title, ptr});
|
||||
}
|
||||
return *m_layouts[title];
|
||||
}
|
||||
|
||||
ShuffleboardLayout& ShuffleboardContainer::GetLayout(std::string_view title) {
|
||||
if (m_layouts.count(title) == 0) {
|
||||
throw FRC_MakeError(err::InvalidParameter,
|
||||
"No layout with title {} has been defined", title);
|
||||
}
|
||||
return *m_layouts[title];
|
||||
}
|
||||
|
||||
ComplexWidget& ShuffleboardContainer::Add(std::string_view title,
|
||||
wpi::Sendable& sendable) {
|
||||
CheckTitle(title);
|
||||
auto widget = std::make_unique<ComplexWidget>(*this, title, sendable);
|
||||
auto ptr = widget.get();
|
||||
m_components.emplace_back(std::move(widget));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
ComplexWidget& ShuffleboardContainer::Add(wpi::Sendable& sendable) {
|
||||
auto name = wpi::SendableRegistry::GetName(&sendable);
|
||||
if (name.empty()) {
|
||||
FRC_ReportError(err::Error, "Sendable must have a name");
|
||||
}
|
||||
return Add(name, sendable);
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
|
||||
const nt::Value& defaultValue) {
|
||||
CheckTitle(title);
|
||||
|
||||
auto widget = std::make_unique<SimpleWidget>(*this, title);
|
||||
auto ptr = widget.get();
|
||||
m_components.emplace_back(std::move(widget));
|
||||
ptr->GetEntry(nt::GetStringFromType(defaultValue.type()))
|
||||
->SetDefault(defaultValue);
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
|
||||
bool defaultValue) {
|
||||
return Add(title, nt::Value::MakeBoolean(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
|
||||
double defaultValue) {
|
||||
return Add(title, nt::Value::MakeDouble(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
|
||||
float defaultValue) {
|
||||
return Add(title, nt::Value::MakeFloat(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
|
||||
int defaultValue) {
|
||||
return Add(title, nt::Value::MakeInteger(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
|
||||
std::string_view defaultValue) {
|
||||
return Add(title, nt::Value::MakeString(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
|
||||
const char* defaultValue) {
|
||||
return Add(title, nt::Value::MakeString(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
|
||||
std::span<const bool> defaultValue) {
|
||||
return Add(title, nt::Value::MakeBooleanArray(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
|
||||
std::span<const double> defaultValue) {
|
||||
return Add(title, nt::Value::MakeDoubleArray(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
|
||||
std::span<const float> defaultValue) {
|
||||
return Add(title, nt::Value::MakeFloatArray(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::Add(
|
||||
std::string_view title, std::span<const int64_t> defaultValue) {
|
||||
return Add(title, nt::Value::MakeIntegerArray(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::Add(
|
||||
std::string_view title, std::span<const std::string> defaultValue) {
|
||||
return Add(title, nt::Value::MakeStringArray(defaultValue));
|
||||
}
|
||||
|
||||
SuppliedValueWidget<std::string>& ShuffleboardContainer::AddString(
|
||||
std::string_view title, std::function<std::string()> supplier) {
|
||||
static auto setter = [](nt::GenericPublisher& entry, std::string value) {
|
||||
entry.SetString(value);
|
||||
};
|
||||
|
||||
CheckTitle(title);
|
||||
auto widget = std::make_unique<SuppliedValueWidget<std::string>>(
|
||||
*this, title, "string", supplier, setter);
|
||||
auto ptr = widget.get();
|
||||
m_components.emplace_back(std::move(widget));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
SuppliedValueWidget<double>& ShuffleboardContainer::AddNumber(
|
||||
std::string_view title, std::function<double()> supplier) {
|
||||
return AddDouble(title, std::move(supplier));
|
||||
}
|
||||
|
||||
SuppliedValueWidget<double>& ShuffleboardContainer::AddDouble(
|
||||
std::string_view title, std::function<double()> supplier) {
|
||||
static auto setter = [](nt::GenericPublisher& entry, double value) {
|
||||
entry.SetDouble(value);
|
||||
};
|
||||
|
||||
CheckTitle(title);
|
||||
auto widget = std::make_unique<SuppliedValueWidget<double>>(
|
||||
*this, title, "double", supplier, setter);
|
||||
auto ptr = widget.get();
|
||||
m_components.emplace_back(std::move(widget));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
SuppliedValueWidget<float>& ShuffleboardContainer::AddFloat(
|
||||
std::string_view title, std::function<float()> supplier) {
|
||||
static auto setter = [](nt::GenericPublisher& entry, float value) {
|
||||
entry.SetFloat(value);
|
||||
};
|
||||
|
||||
CheckTitle(title);
|
||||
auto widget = std::make_unique<SuppliedValueWidget<float>>(
|
||||
*this, title, "float", supplier, setter);
|
||||
auto ptr = widget.get();
|
||||
m_components.emplace_back(std::move(widget));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
SuppliedValueWidget<int64_t>& ShuffleboardContainer::AddInteger(
|
||||
std::string_view title, std::function<int64_t()> supplier) {
|
||||
static auto setter = [](nt::GenericPublisher& entry, int64_t value) {
|
||||
entry.SetInteger(value);
|
||||
};
|
||||
|
||||
CheckTitle(title);
|
||||
auto widget = std::make_unique<SuppliedValueWidget<int64_t>>(
|
||||
*this, title, "int", supplier, setter);
|
||||
auto ptr = widget.get();
|
||||
m_components.emplace_back(std::move(widget));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
SuppliedValueWidget<bool>& ShuffleboardContainer::AddBoolean(
|
||||
std::string_view title, std::function<bool()> supplier) {
|
||||
static auto setter = [](nt::GenericPublisher& entry, bool value) {
|
||||
entry.SetBoolean(value);
|
||||
};
|
||||
|
||||
CheckTitle(title);
|
||||
auto widget = std::make_unique<SuppliedValueWidget<bool>>(
|
||||
*this, title, "boolean", supplier, setter);
|
||||
auto ptr = widget.get();
|
||||
m_components.emplace_back(std::move(widget));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
SuppliedValueWidget<std::vector<std::string>>&
|
||||
ShuffleboardContainer::AddStringArray(
|
||||
std::string_view title,
|
||||
std::function<std::vector<std::string>()> supplier) {
|
||||
static auto setter = [](nt::GenericPublisher& entry,
|
||||
std::vector<std::string> value) {
|
||||
entry.SetStringArray(value);
|
||||
};
|
||||
|
||||
CheckTitle(title);
|
||||
auto widget = std::make_unique<SuppliedValueWidget<std::vector<std::string>>>(
|
||||
*this, title, "string[]", supplier, setter);
|
||||
auto ptr = widget.get();
|
||||
m_components.emplace_back(std::move(widget));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
SuppliedValueWidget<std::vector<double>>& ShuffleboardContainer::AddNumberArray(
|
||||
std::string_view title, std::function<std::vector<double>()> supplier) {
|
||||
return AddDoubleArray(title, std::move(supplier));
|
||||
}
|
||||
|
||||
SuppliedValueWidget<std::vector<double>>& ShuffleboardContainer::AddDoubleArray(
|
||||
std::string_view title, std::function<std::vector<double>()> supplier) {
|
||||
static auto setter = [](nt::GenericPublisher& entry,
|
||||
std::vector<double> value) {
|
||||
entry.SetDoubleArray(value);
|
||||
};
|
||||
|
||||
CheckTitle(title);
|
||||
auto widget = std::make_unique<SuppliedValueWidget<std::vector<double>>>(
|
||||
*this, title, "double[]", supplier, setter);
|
||||
auto ptr = widget.get();
|
||||
m_components.emplace_back(std::move(widget));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
SuppliedValueWidget<std::vector<float>>& ShuffleboardContainer::AddFloatArray(
|
||||
std::string_view title, std::function<std::vector<float>()> supplier) {
|
||||
static auto setter = [](nt::GenericPublisher& entry,
|
||||
std::vector<float> value) {
|
||||
entry.SetFloatArray(value);
|
||||
};
|
||||
|
||||
CheckTitle(title);
|
||||
auto widget = std::make_unique<SuppliedValueWidget<std::vector<float>>>(
|
||||
*this, title, "float[]", supplier, setter);
|
||||
auto ptr = widget.get();
|
||||
m_components.emplace_back(std::move(widget));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
SuppliedValueWidget<std::vector<int64_t>>&
|
||||
ShuffleboardContainer::AddIntegerArray(
|
||||
std::string_view title, std::function<std::vector<int64_t>()> supplier) {
|
||||
static auto setter = [](nt::GenericPublisher& entry,
|
||||
std::vector<int64_t> value) {
|
||||
entry.SetIntegerArray(value);
|
||||
};
|
||||
|
||||
CheckTitle(title);
|
||||
auto widget = std::make_unique<SuppliedValueWidget<std::vector<int64_t>>>(
|
||||
*this, title, "int[]", supplier, setter);
|
||||
auto ptr = widget.get();
|
||||
m_components.emplace_back(std::move(widget));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
SuppliedValueWidget<std::vector<int>>& ShuffleboardContainer::AddBooleanArray(
|
||||
std::string_view title, std::function<std::vector<int>()> supplier) {
|
||||
static auto setter = [](nt::GenericPublisher& entry, std::vector<int> value) {
|
||||
entry.SetBooleanArray(value);
|
||||
};
|
||||
|
||||
CheckTitle(title);
|
||||
auto widget = std::make_unique<SuppliedValueWidget<std::vector<int>>>(
|
||||
*this, title, "boolean[]", supplier, setter);
|
||||
auto ptr = widget.get();
|
||||
m_components.emplace_back(std::move(widget));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
SuppliedValueWidget<std::vector<uint8_t>>& ShuffleboardContainer::AddRaw(
|
||||
std::string_view title, std::function<std::vector<uint8_t>()> supplier) {
|
||||
return AddRaw(title, "raw", std::move(supplier));
|
||||
}
|
||||
|
||||
SuppliedValueWidget<std::vector<uint8_t>>& ShuffleboardContainer::AddRaw(
|
||||
std::string_view title, std::string_view typeString,
|
||||
std::function<std::vector<uint8_t>()> supplier) {
|
||||
static auto setter = [](nt::GenericPublisher& entry,
|
||||
std::vector<uint8_t> value) { entry.SetRaw(value); };
|
||||
|
||||
CheckTitle(title);
|
||||
auto widget = std::make_unique<SuppliedValueWidget<std::vector<uint8_t>>>(
|
||||
*this, title, typeString, supplier, setter);
|
||||
auto ptr = widget.get();
|
||||
m_components.emplace_back(std::move(widget));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::AddPersistent(
|
||||
std::string_view title, const nt::Value& defaultValue) {
|
||||
auto& widget = Add(title, defaultValue);
|
||||
widget.GetEntry(nt::GetStringFromType(defaultValue.type()))
|
||||
->GetTopic()
|
||||
.SetPersistent(true);
|
||||
return widget;
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::AddPersistent(std::string_view title,
|
||||
bool defaultValue) {
|
||||
return AddPersistent(title, nt::Value::MakeBoolean(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::AddPersistent(std::string_view title,
|
||||
double defaultValue) {
|
||||
return AddPersistent(title, nt::Value::MakeDouble(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::AddPersistent(std::string_view title,
|
||||
float defaultValue) {
|
||||
return AddPersistent(title, nt::Value::MakeFloat(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::AddPersistent(std::string_view title,
|
||||
int defaultValue) {
|
||||
return AddPersistent(title, nt::Value::MakeInteger(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::AddPersistent(
|
||||
std::string_view title, std::string_view defaultValue) {
|
||||
return AddPersistent(title, nt::Value::MakeString(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::AddPersistent(
|
||||
std::string_view title, std::span<const bool> defaultValue) {
|
||||
return AddPersistent(title, nt::Value::MakeBooleanArray(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::AddPersistent(
|
||||
std::string_view title, std::span<const double> defaultValue) {
|
||||
return AddPersistent(title, nt::Value::MakeDoubleArray(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::AddPersistent(
|
||||
std::string_view title, std::span<const float> defaultValue) {
|
||||
return AddPersistent(title, nt::Value::MakeFloatArray(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::AddPersistent(
|
||||
std::string_view title, std::span<const int64_t> defaultValue) {
|
||||
return AddPersistent(title, nt::Value::MakeIntegerArray(defaultValue));
|
||||
}
|
||||
|
||||
SimpleWidget& ShuffleboardContainer::AddPersistent(
|
||||
std::string_view title, std::span<const std::string> defaultValue) {
|
||||
return AddPersistent(title, nt::Value::MakeStringArray(defaultValue));
|
||||
}
|
||||
|
||||
void ShuffleboardContainer::EnableIfActuator() {
|
||||
for (auto& component : GetComponents()) {
|
||||
component->EnableIfActuator();
|
||||
}
|
||||
}
|
||||
|
||||
void ShuffleboardContainer::DisableIfActuator() {
|
||||
for (auto& component : GetComponents()) {
|
||||
component->DisableIfActuator();
|
||||
}
|
||||
}
|
||||
|
||||
void ShuffleboardContainer::CheckTitle(std::string_view title) {
|
||||
std::string titleStr{title};
|
||||
if (m_usedTitles.count(titleStr) > 0) {
|
||||
FRC_ReportError(err::Error, "Title is already in use: {}", title);
|
||||
return;
|
||||
}
|
||||
m_usedTitles.insert(titleStr);
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardInstance.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <hal/FRCUsageReporting.h>
|
||||
#include <networktables/NetworkTable.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/StringMap.h>
|
||||
|
||||
#include "frc/shuffleboard/Shuffleboard.h"
|
||||
|
||||
using namespace frc::detail;
|
||||
|
||||
struct ShuffleboardInstance::Impl {
|
||||
wpi::StringMap<ShuffleboardTab> tabs;
|
||||
|
||||
bool tabsChanged = false;
|
||||
std::shared_ptr<nt::NetworkTable> rootTable;
|
||||
std::shared_ptr<nt::NetworkTable> rootMetaTable;
|
||||
nt::StringPublisher selectedTabPub;
|
||||
};
|
||||
|
||||
ShuffleboardInstance::ShuffleboardInstance(nt::NetworkTableInstance ntInstance)
|
||||
: m_impl(new Impl) {
|
||||
m_impl->rootTable = ntInstance.GetTable(Shuffleboard::kBaseTableName);
|
||||
m_impl->rootMetaTable = m_impl->rootTable->GetSubTable(".metadata");
|
||||
m_impl->selectedTabPub =
|
||||
m_impl->rootMetaTable->GetStringTopic("Selected")
|
||||
.Publish(nt::PubSubOptions{.keepDuplicates = true});
|
||||
}
|
||||
|
||||
ShuffleboardInstance::~ShuffleboardInstance() = default;
|
||||
|
||||
static bool gReported = false;
|
||||
|
||||
frc::ShuffleboardTab& ShuffleboardInstance::GetTab(std::string_view title) {
|
||||
if (!gReported) {
|
||||
HAL_Report(HALUsageReporting::kResourceType_Shuffleboard, 0);
|
||||
gReported = true;
|
||||
}
|
||||
auto [it, added] = m_impl->tabs.try_emplace(title, *this, title);
|
||||
if (added) {
|
||||
m_impl->tabsChanged = true;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void ShuffleboardInstance::Update() {
|
||||
if (m_impl->tabsChanged) {
|
||||
wpi::SmallVector<std::string, 16> tabTitles;
|
||||
for (auto& entry : m_impl->tabs) {
|
||||
tabTitles.emplace_back(entry.second.GetTitle());
|
||||
}
|
||||
m_impl->rootMetaTable->GetEntry("Tabs").SetStringArray(tabTitles);
|
||||
m_impl->tabsChanged = false;
|
||||
}
|
||||
for (auto& entry : m_impl->tabs) {
|
||||
auto& tab = entry.second;
|
||||
tab.BuildInto(m_impl->rootTable,
|
||||
m_impl->rootMetaTable->GetSubTable(tab.GetTitle()));
|
||||
}
|
||||
}
|
||||
|
||||
void ShuffleboardInstance::EnableActuatorWidgets() {
|
||||
for (auto& entry : m_impl->tabs) {
|
||||
auto& tab = entry.second;
|
||||
for (auto& component : tab.GetComponents()) {
|
||||
component->EnableIfActuator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShuffleboardInstance::DisableActuatorWidgets() {
|
||||
for (auto& entry : m_impl->tabs) {
|
||||
auto& tab = entry.second;
|
||||
for (auto& component : tab.GetComponents()) {
|
||||
component->DisableIfActuator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShuffleboardInstance::SelectTab(int index) {
|
||||
m_impl->selectedTabPub.Set(std::to_string(index));
|
||||
}
|
||||
|
||||
void ShuffleboardInstance::SelectTab(std::string_view title) {
|
||||
m_impl->selectedTabPub.Set(title);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardLayout.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <wpi/json.h>
|
||||
|
||||
using namespace frc;
|
||||
|
||||
static constexpr std::string_view kSmartDashboardType = "ShuffleboardLayout";
|
||||
|
||||
ShuffleboardLayout::ShuffleboardLayout(ShuffleboardContainer& parent,
|
||||
std::string_view title,
|
||||
std::string_view type)
|
||||
: ShuffleboardValue(title),
|
||||
ShuffleboardComponent(parent, title, type),
|
||||
ShuffleboardContainer(title) {
|
||||
m_isLayout = true;
|
||||
}
|
||||
|
||||
void ShuffleboardLayout::BuildInto(
|
||||
std::shared_ptr<nt::NetworkTable> parentTable,
|
||||
std::shared_ptr<nt::NetworkTable> metaTable) {
|
||||
BuildMetadata(metaTable);
|
||||
auto table = parentTable->GetSubTable(GetTitle());
|
||||
table->GetEntry(".type").SetString(kSmartDashboardType);
|
||||
table->GetEntry(".type").GetTopic().SetProperty("SmartDashboard",
|
||||
kSmartDashboardType);
|
||||
for (auto& component : GetComponents()) {
|
||||
component->BuildInto(table, metaTable->GetSubTable(component->GetTitle()));
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardTab.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <wpi/json.h>
|
||||
|
||||
using namespace frc;
|
||||
|
||||
static constexpr std::string_view kSmartDashboardType = "ShuffleboardLayout";
|
||||
|
||||
ShuffleboardTab::ShuffleboardTab(ShuffleboardRoot& root, std::string_view title)
|
||||
: ShuffleboardValue(title), ShuffleboardContainer(title), m_root(root) {}
|
||||
|
||||
ShuffleboardRoot& ShuffleboardTab::GetRoot() {
|
||||
return m_root;
|
||||
}
|
||||
|
||||
void ShuffleboardTab::BuildInto(std::shared_ptr<nt::NetworkTable> parentTable,
|
||||
std::shared_ptr<nt::NetworkTable> metaTable) {
|
||||
auto tabTable = parentTable->GetSubTable(GetTitle());
|
||||
tabTable->GetEntry(".type").SetString(kSmartDashboardType);
|
||||
tabTable->GetEntry(".type").GetTopic().SetProperty("SmartDashboard",
|
||||
kSmartDashboardType);
|
||||
for (auto& component : GetComponents()) {
|
||||
component->BuildInto(tabTable,
|
||||
metaTable->GetSubTable(component->GetTitle()));
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardWidget.h"
|
||||
|
||||
using namespace frc;
|
||||
|
||||
static constexpr const char* widgetStrings[] = {
|
||||
"Text View",
|
||||
"Number Slider",
|
||||
"Number Bar",
|
||||
"Simple Dial",
|
||||
"Graph",
|
||||
"Boolean Box",
|
||||
"Toggle Button",
|
||||
"Toggle Switch",
|
||||
"Voltage View",
|
||||
"PDP",
|
||||
"ComboBox Chooser",
|
||||
"Split Button Chooser",
|
||||
"Encoder",
|
||||
"Motor Controller",
|
||||
"Command",
|
||||
"PID Command",
|
||||
"PID Controller",
|
||||
"Accelerometer",
|
||||
"3-Axis Accelerometer",
|
||||
"Gyro",
|
||||
"Relay",
|
||||
"Differential Drivebase",
|
||||
"Mecanum Drivebase",
|
||||
"Camera Stream",
|
||||
"Field",
|
||||
};
|
||||
|
||||
const char* detail::GetStringForWidgetType(BuiltInWidgets type) {
|
||||
return widgetStrings[static_cast<int>(type)];
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/SimpleWidget.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "frc/shuffleboard/Shuffleboard.h"
|
||||
#include "frc/shuffleboard/ShuffleboardLayout.h"
|
||||
#include "frc/shuffleboard/ShuffleboardTab.h"
|
||||
|
||||
using namespace frc;
|
||||
|
||||
SimpleWidget::SimpleWidget(ShuffleboardContainer& parent,
|
||||
std::string_view title)
|
||||
: ShuffleboardValue(title), ShuffleboardWidget(parent, title), m_entry() {}
|
||||
|
||||
nt::GenericEntry* SimpleWidget::GetEntry() {
|
||||
if (!m_entry) {
|
||||
ForceGenerate();
|
||||
}
|
||||
return &m_entry;
|
||||
}
|
||||
|
||||
nt::GenericEntry* SimpleWidget::GetEntry(std::string_view typeString) {
|
||||
if (!m_entry) {
|
||||
m_typeString = typeString;
|
||||
ForceGenerate();
|
||||
}
|
||||
return &m_entry;
|
||||
}
|
||||
|
||||
void SimpleWidget::BuildInto(std::shared_ptr<nt::NetworkTable> parentTable,
|
||||
std::shared_ptr<nt::NetworkTable> metaTable) {
|
||||
BuildMetadata(metaTable);
|
||||
if (!m_entry) {
|
||||
m_entry = parentTable->GetTopic(GetTitle()).GetGenericEntry(m_typeString);
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleWidget::ForceGenerate() {
|
||||
ShuffleboardContainer* parent = &GetParent();
|
||||
|
||||
while (parent->m_isLayout) {
|
||||
parent = &(static_cast<ShuffleboardLayout*>(parent)->GetParent());
|
||||
}
|
||||
|
||||
auto& tab = *static_cast<ShuffleboardTab*>(parent);
|
||||
tab.GetRoot().Update();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/WidgetType.h"
|
||||
|
||||
using namespace frc;
|
||||
|
||||
std::string_view WidgetType::GetWidgetName() const {
|
||||
return m_widgetName;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this 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>
|
||||
*/
|
||||
enum class BuiltInLayouts {
|
||||
/**
|
||||
* Groups components in a vertical list. New widgets added to the layout will
|
||||
* be placed at the bottom of the list. <br>Custom properties: <table>
|
||||
* <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,
|
||||
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,385 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this 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.
|
||||
*/
|
||||
enum class BuiltInWidgets {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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>
|
||||
*/
|
||||
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>
|
||||
*/
|
||||
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>
|
||||
*/
|
||||
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>
|
||||
*/
|
||||
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>
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
kToggleSwitch,
|
||||
/**
|
||||
* Displays an analog input or a raw number with a number bar.
|
||||
* <br>Supported types:
|
||||
* <ul>
|
||||
* <li>Number</li>
|
||||
* <li>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>
|
||||
*/
|
||||
kVoltageView,
|
||||
/**
|
||||
* Displays a PowerDistribution. <br>Supported types: <ul> <li>
|
||||
* PowerDistribution</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>
|
||||
*/
|
||||
kPowerDistribution,
|
||||
/**
|
||||
* Displays a SendableChooser with a dropdown combo box with a list of
|
||||
* options.
|
||||
* <br>Supported types:
|
||||
* <ul>
|
||||
* <li>SendableChooser</li>
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
kComboBoxChooser,
|
||||
/**
|
||||
* Displays a SendableChooserwith a toggle button for each available option.
|
||||
* <br>Supported types:
|
||||
* <ul>
|
||||
* <li>SendableChooser</li>
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
kSplitButtonChooser,
|
||||
/**
|
||||
* Displays an Encoder displaying its speed,
|
||||
* total traveled distance, and its distance per tick. <br>Supported types:
|
||||
* <ul>
|
||||
* <li>Encoder</li>
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
kEncoder,
|
||||
/**
|
||||
* Displays a MotorController.
|
||||
* The motor controller will be controllable from the dashboard when test mode
|
||||
* is enabled, but will otherwise be view-only. <br>Supported types: <ul>
|
||||
* <li>PWMMotorController</li>
|
||||
* <li>DMC60</li>
|
||||
* <li>Jaguar</li>
|
||||
* <li>PWMTalonSRX</li>
|
||||
* <li>PWMVictorSPX</li>
|
||||
* <li>SD540</li>
|
||||
* <li>Spark</li>
|
||||
* <li>Talon</li>
|
||||
* <li>Victor</li>
|
||||
* <li>VictorSP</li>
|
||||
* <li>MotorControllerGroup</li>
|
||||
* <li>Any custom subclass of {@code SpeedContorller}</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>
|
||||
*/
|
||||
kMotorController,
|
||||
/**
|
||||
* 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>Command</li> <li>CommandGroup</li>
|
||||
* <li>Any custom subclass of {@code Command} or {@code CommandGroup}</li>
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
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>PIDCommand</li>
|
||||
* <li>Any custom subclass of {@code PIDCommand}</li>
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
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>PIDController</li>
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
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>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>
|
||||
*/
|
||||
kAccelerometer,
|
||||
/**
|
||||
* Displays a 3-axis accelerometer with a number bar for each axis'
|
||||
* acceleration. <br>Supported types: <ul> <li>ADXL345_I2C</li> <li>
|
||||
* ADXL345_SPI</li> <li>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>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>
|
||||
*/
|
||||
k3AxisAccelerometer,
|
||||
/**
|
||||
* Displays a gyro with a dial from 0 to 360 degrees.
|
||||
* <br>Supported types:
|
||||
* <ul>
|
||||
* <li>ADXRS450_Gyro</li>
|
||||
* <li>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>
|
||||
*/
|
||||
kGyro,
|
||||
/**
|
||||
* Displays a relay with toggle buttons for each supported mode (off, on,
|
||||
* forward, reverse). <br>Supported types: <ul> <li>Relay</li>
|
||||
* </ul>
|
||||
* <br>This widget has no custom properties.
|
||||
*/
|
||||
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>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>
|
||||
*/
|
||||
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>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>
|
||||
*/
|
||||
kMecanumDrive,
|
||||
/**
|
||||
* Displays a camera stream.
|
||||
* <br>Supported types:
|
||||
* <ul>
|
||||
* <li>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>
|
||||
*/
|
||||
kCameraStream,
|
||||
/**
|
||||
* Displays a field2d object.<br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Field2d
|
||||
* </ul>
|
||||
*/
|
||||
kField,
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include <networktables/NetworkTable.h>
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardWidget.h"
|
||||
|
||||
namespace wpi {
|
||||
class Sendable;
|
||||
class SendableBuilder;
|
||||
} // namespace wpi
|
||||
|
||||
namespace frc {
|
||||
|
||||
class ShuffleboardContainer;
|
||||
|
||||
/**
|
||||
* A Shuffleboard widget that handles a Sendable object such as a motor
|
||||
* controller or sensor.
|
||||
*/
|
||||
class ComplexWidget final : public ShuffleboardWidget<ComplexWidget> {
|
||||
public:
|
||||
ComplexWidget(ShuffleboardContainer& parent, std::string_view title,
|
||||
wpi::Sendable& sendable);
|
||||
|
||||
~ComplexWidget() override;
|
||||
|
||||
void EnableIfActuator() override;
|
||||
|
||||
void DisableIfActuator() override;
|
||||
|
||||
void BuildInto(std::shared_ptr<nt::NetworkTable> parentTable,
|
||||
std::shared_ptr<nt::NetworkTable> metaTable) override;
|
||||
|
||||
private:
|
||||
wpi::Sendable& m_sendable;
|
||||
std::unique_ptr<wpi::SendableBuilder> m_builder;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
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 constexpr LayoutType(const char* layoutName)
|
||||
: m_layoutName(layoutName) {}
|
||||
~LayoutType() = default;
|
||||
|
||||
/**
|
||||
* Gets the string type of the layout as defined by that layout in
|
||||
* Shuffleboard.
|
||||
*/
|
||||
std::string_view GetLayoutName() const;
|
||||
|
||||
private:
|
||||
const char* m_layoutName;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <networktables/BooleanTopic.h>
|
||||
#include <networktables/NetworkTable.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <networktables/StringTopic.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardEventImportance.h"
|
||||
|
||||
namespace frc::detail {
|
||||
|
||||
class RecordingController final {
|
||||
public:
|
||||
explicit RecordingController(nt::NetworkTableInstance ntInstance);
|
||||
virtual ~RecordingController() = default;
|
||||
|
||||
void StartRecording();
|
||||
void StopRecording();
|
||||
void SetRecordingFileNameFormat(std::string_view format);
|
||||
void ClearRecordingFileNameFormat();
|
||||
|
||||
void AddEventMarker(std::string_view name, std::string_view description,
|
||||
ShuffleboardEventImportance importance);
|
||||
|
||||
private:
|
||||
nt::BooleanPublisher m_recordingControlEntry;
|
||||
nt::StringPublisher m_recordingFileNameFormatEntry;
|
||||
std::shared_ptr<nt::NetworkTable> m_eventsTable;
|
||||
};
|
||||
|
||||
} // namespace frc::detail
|
||||
@@ -1,136 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#ifndef DYNAMIC_CAMERA_SERVER
|
||||
#include <cscore_oo.h>
|
||||
#include <fmt/format.h>
|
||||
#include <networktables/NetworkTable.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#else
|
||||
namespace cs {
|
||||
class VideoSource;
|
||||
} // namespace cs
|
||||
using CS_Handle = int; // NOLINT
|
||||
using CS_Source = CS_Handle; // NOLINT
|
||||
#endif
|
||||
|
||||
#include <networktables/StringArrayTopic.h>
|
||||
#include <wpi/sendable/Sendable.h>
|
||||
#include <wpi/sendable/SendableHelper.h>
|
||||
|
||||
namespace frc {
|
||||
|
||||
class SendableCameraWrapper;
|
||||
|
||||
namespace detail {
|
||||
constexpr const char* kProtocol = "camera_server://";
|
||||
std::shared_ptr<SendableCameraWrapper>& GetSendableCameraWrapper(
|
||||
std::string_view cameraName);
|
||||
void AddToSendableRegistry(wpi::Sendable* sendable, std::string_view name);
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* A wrapper to make video sources sendable and usable from Shuffleboard.
|
||||
*/
|
||||
class SendableCameraWrapper
|
||||
: public wpi::Sendable,
|
||||
public wpi::SendableHelper<SendableCameraWrapper> {
|
||||
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 cameraName the name of the camera to wrap
|
||||
*/
|
||||
SendableCameraWrapper(std::string_view cameraName, const private_init&);
|
||||
|
||||
/**
|
||||
* Creates a new sendable wrapper. Private constructor to avoid direct
|
||||
* instantiation with multiple wrappers floating around for the same camera.
|
||||
*
|
||||
* @param cameraName the name of the camera to wrap
|
||||
* @param cameraUrls camera URLs
|
||||
*/
|
||||
SendableCameraWrapper(std::string_view cameraName,
|
||||
std::span<const std::string> cameraUrls,
|
||||
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);
|
||||
|
||||
static SendableCameraWrapper& Wrap(std::string_view cameraName,
|
||||
std::span<const std::string> cameraUrls);
|
||||
|
||||
void InitSendable(wpi::SendableBuilder& builder) override;
|
||||
|
||||
private:
|
||||
std::string m_uri;
|
||||
nt::StringArrayPublisher m_streams;
|
||||
};
|
||||
|
||||
#ifndef DYNAMIC_CAMERA_SERVER
|
||||
inline SendableCameraWrapper::SendableCameraWrapper(std::string_view name,
|
||||
const private_init&)
|
||||
: m_uri(detail::kProtocol) {
|
||||
detail::AddToSendableRegistry(this, name);
|
||||
m_uri += name;
|
||||
}
|
||||
|
||||
inline SendableCameraWrapper::SendableCameraWrapper(
|
||||
std::string_view cameraName, std::span<const std::string> cameraUrls,
|
||||
const private_init&)
|
||||
: SendableCameraWrapper(cameraName, private_init{}) {
|
||||
m_streams = nt::NetworkTableInstance::GetDefault()
|
||||
.GetStringArrayTopic(
|
||||
fmt::format("/CameraPublisher/{}/streams", cameraName))
|
||||
.Publish();
|
||||
m_streams.Set(cameraUrls);
|
||||
}
|
||||
|
||||
inline SendableCameraWrapper& SendableCameraWrapper::Wrap(
|
||||
const cs::VideoSource& source) {
|
||||
return Wrap(source.GetHandle());
|
||||
}
|
||||
|
||||
inline SendableCameraWrapper& SendableCameraWrapper::Wrap(CS_Source source) {
|
||||
CS_Status status = 0;
|
||||
auto name = cs::GetSourceName(source, &status);
|
||||
auto& wrapper = detail::GetSendableCameraWrapper(name);
|
||||
if (!wrapper) {
|
||||
wrapper = std::make_shared<SendableCameraWrapper>(name, private_init{});
|
||||
}
|
||||
return *wrapper;
|
||||
}
|
||||
|
||||
inline SendableCameraWrapper& SendableCameraWrapper::Wrap(
|
||||
std::string_view cameraName, std::span<const std::string> cameraUrls) {
|
||||
auto& wrapper = detail::GetSendableCameraWrapper(cameraName);
|
||||
if (!wrapper) {
|
||||
wrapper = std::make_shared<SendableCameraWrapper>(cameraName, cameraUrls,
|
||||
private_init{});
|
||||
}
|
||||
return *wrapper;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,196 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "frc/shuffleboard/RecordingController.h"
|
||||
#include "frc/shuffleboard/ShuffleboardEventImportance.h"
|
||||
#include "frc/shuffleboard/ShuffleboardInstance.h"
|
||||
|
||||
namespace frc {
|
||||
|
||||
class ShuffleboardTab;
|
||||
|
||||
/**
|
||||
* The Shuffleboard class provides a mechanism with which data can be added and
|
||||
* laid out in the Shuffleboard dashboard application from a robot program. Tabs
|
||||
* and layouts can be specified, as well as choosing which widgets to display
|
||||
* with and setting properties of these widgets; for example, programmers can
|
||||
* specify a specific {@code boolean} value to be displayed with a toggle button
|
||||
* instead of the default colored box, or set custom colors for that box.
|
||||
*
|
||||
* For example, displaying a boolean entry with a toggle button:
|
||||
* <pre>{@code
|
||||
* NetworkTableEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* .add("My Boolean", false)
|
||||
* .withWidget("Toggle Button")
|
||||
* .getEntry();
|
||||
* }</pre>
|
||||
*
|
||||
* Changing the colors of the boolean box:
|
||||
* <pre>{@code
|
||||
* NetworkTableEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* .add("My Boolean", false)
|
||||
* .withWidget("Boolean Box")
|
||||
* .withProperties(Map.of("colorWhenTrue", "green", "colorWhenFalse",
|
||||
* "maroon")) .getEntry();
|
||||
* }</pre>
|
||||
*
|
||||
* Specifying a parent layout. Note that the layout type must <i>always</i> be
|
||||
* specified, even if the layout has already been generated by a previously
|
||||
* defined entry.
|
||||
* <pre>{@code
|
||||
* NetworkTableEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* .getLayout("List", "Example List")
|
||||
* .add("My Boolean", false)
|
||||
* .withWidget("Toggle Button")
|
||||
* .getEntry();
|
||||
* }</pre>
|
||||
* </p>
|
||||
*
|
||||
* Teams are encouraged to set up shuffleboard layouts at the start of the robot
|
||||
* program.
|
||||
*/
|
||||
class Shuffleboard final {
|
||||
public:
|
||||
/**
|
||||
* The name of the base NetworkTable into which all Shuffleboard data will be
|
||||
* added.
|
||||
*/
|
||||
static constexpr const char* kBaseTableName = "/Shuffleboard";
|
||||
|
||||
/**
|
||||
* Updates all the values in Shuffleboard. Iterative and timed robots are
|
||||
* pre-configured to call this method in the main robot loop; teams using
|
||||
* custom robot base classes, or subclass SampleRobot, should make sure to
|
||||
* call this repeatedly to keep data on the dashboard up to date.
|
||||
*/
|
||||
static void Update();
|
||||
|
||||
/**
|
||||
* Gets the Shuffleboard tab with the given title, creating it if it does not
|
||||
* already exist.
|
||||
*
|
||||
* @param title the title of the tab
|
||||
* @return the tab with the given title
|
||||
*/
|
||||
static ShuffleboardTab& GetTab(std::string_view title);
|
||||
|
||||
/**
|
||||
* Selects the tab in the dashboard with the given index in the range
|
||||
* [0..n-1], where <i>n</i> is the number of tabs in the dashboard at the time
|
||||
* this method is called.
|
||||
*
|
||||
* @param index the index of the tab to select
|
||||
*/
|
||||
static void SelectTab(int index);
|
||||
|
||||
/**
|
||||
* Selects the tab in the dashboard with the given title.
|
||||
*
|
||||
* @param title the title of the tab to select
|
||||
*/
|
||||
static void SelectTab(std::string_view title);
|
||||
|
||||
/**
|
||||
* Enables user control of widgets containing actuators: motor controllers,
|
||||
* relays, etc. This should only be used when the robot is in test mode.
|
||||
* IterativeRobotBase and SampleRobot are both configured to call this method
|
||||
* when entering test mode; most users should not need to use this method
|
||||
* directly.
|
||||
*/
|
||||
static void EnableActuatorWidgets();
|
||||
|
||||
/**
|
||||
* Disables user control of widgets containing actuators. For safety reasons,
|
||||
* actuators should only be controlled while in test mode. IterativeRobotBase
|
||||
* and SampleRobot are both configured to call this method when exiting in
|
||||
* test mode; most users should not need to use this method directly.
|
||||
*/
|
||||
static void DisableActuatorWidgets();
|
||||
|
||||
/**
|
||||
* Starts data recording on the dashboard. Has no effect if recording is
|
||||
* already in progress.
|
||||
*/
|
||||
static void StartRecording();
|
||||
|
||||
/**
|
||||
* Stops data recording on the dashboard. Has no effect if no recording is in
|
||||
* progress.
|
||||
*/
|
||||
static void StopRecording();
|
||||
|
||||
/**
|
||||
* Sets the file name format for new recording files to use. If recording is
|
||||
* in progress when this method is called, it will continue to use the same
|
||||
* file. New recordings will use the format.
|
||||
*
|
||||
* <p>To avoid recording files overwriting each other, make sure to use unique
|
||||
* recording file names. File name formats accept templates for inserting the
|
||||
* date and time when the recording started with the {@code ${date}} and
|
||||
* {@code ${time}} templates, respectively. For example, the default format is
|
||||
* {@code "recording-${time}"} and recording files created with it will have
|
||||
* names like {@code "recording-2018.01.15.sbr"}. Users are
|
||||
* <strong>strongly</strong> recommended to use the {@code ${time}} template
|
||||
* to ensure unique file names.
|
||||
* </p>
|
||||
*
|
||||
* @param format the format for the
|
||||
*/
|
||||
static void SetRecordingFileNameFormat(std::string_view format);
|
||||
|
||||
/**
|
||||
* Clears the custom name format for recording files. New recordings will use
|
||||
* the default format.
|
||||
*
|
||||
* @see SetRecordingFileNameFormat(std::string_view)
|
||||
*/
|
||||
static void ClearRecordingFileNameFormat();
|
||||
|
||||
/**
|
||||
* Notifies Shuffleboard of an event. Events can range from as trivial as a
|
||||
* change in a command state to as critical as a total power loss or component
|
||||
* failure. If Shuffleboard is recording, the event will also be recorded.
|
||||
*
|
||||
* <p>If {@code name} is {@code null} or empty, no event will be sent and an
|
||||
* error will be printed to the driver station.
|
||||
*
|
||||
* @param name the name of the event
|
||||
* @param description a description of the event
|
||||
* @param importance the importance of the event
|
||||
*/
|
||||
static void AddEventMarker(std::string_view name,
|
||||
std::string_view description,
|
||||
ShuffleboardEventImportance importance);
|
||||
|
||||
/**
|
||||
* Notifies Shuffleboard of an event. Events can range from as trivial as a
|
||||
* change in a command state to as critical as a total power loss or component
|
||||
* failure. If Shuffleboard is recording, the event will also be recorded.
|
||||
*
|
||||
* <p>If {@code name} is {@code null} or empty, no event will be sent and an
|
||||
* error will be printed to the driver station.
|
||||
*
|
||||
* @param name the name of the event
|
||||
* @param importance the importance of the event
|
||||
*/
|
||||
static void AddEventMarker(std::string_view name,
|
||||
ShuffleboardEventImportance importance);
|
||||
|
||||
private:
|
||||
static detail::ShuffleboardInstance& GetInstance();
|
||||
static detail::RecordingController& GetRecordingController();
|
||||
|
||||
// TODO usage reporting
|
||||
|
||||
Shuffleboard() = default;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
|
||||
// Make use of references returned by member functions usable
|
||||
#include "frc/shuffleboard/ShuffleboardTab.h"
|
||||
@@ -1,89 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <networktables/NetworkTable.h>
|
||||
#include <networktables/NetworkTableValue.h>
|
||||
#include <wpi/StringMap.h>
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardComponentBase.h"
|
||||
|
||||
namespace frc {
|
||||
|
||||
class ShuffleboardContainer;
|
||||
|
||||
/**
|
||||
* A generic component in Shuffleboard.
|
||||
*
|
||||
* @tparam Derived the self type
|
||||
*/
|
||||
template <typename Derived>
|
||||
class ShuffleboardComponent : public ShuffleboardComponentBase {
|
||||
public:
|
||||
/**
|
||||
* Constructs a ShuffleboardComponent.
|
||||
*
|
||||
* @param parent The parent container.
|
||||
* @param title The component title.
|
||||
* @param type The component type.
|
||||
*/
|
||||
ShuffleboardComponent(ShuffleboardContainer& parent, std::string_view title,
|
||||
std::string_view type = "")
|
||||
: ShuffleboardValue(title),
|
||||
ShuffleboardComponentBase(parent, title, type) {}
|
||||
|
||||
/**
|
||||
* Sets custom properties for this component. Property names are
|
||||
* case-sensitive and whitespace-insensitive (capitalization and spaces do not
|
||||
* matter).
|
||||
*
|
||||
* @param properties the properties for this component
|
||||
* @return this component
|
||||
*/
|
||||
Derived& WithProperties(const wpi::StringMap<nt::Value>& properties) {
|
||||
m_properties = properties;
|
||||
m_metadataDirty = true;
|
||||
return *static_cast<Derived*>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position of this component in the tab. This has no effect if this
|
||||
* component is inside a layout.
|
||||
*
|
||||
* If the position of a single component is set, it is recommended to set the
|
||||
* positions of <i>all</i> components inside a tab to prevent Shuffleboard
|
||||
* from automatically placing another component there before the one with the
|
||||
* specific position is sent.
|
||||
*
|
||||
* @param columnIndex the column in the tab to place this component
|
||||
* @param rowIndex the row in the tab to place this component
|
||||
* @return this component
|
||||
*/
|
||||
Derived& WithPosition(int columnIndex, int rowIndex) {
|
||||
m_column = columnIndex;
|
||||
m_row = rowIndex;
|
||||
m_metadataDirty = true;
|
||||
return *static_cast<Derived*>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of this component in the tab. This has no effect if this
|
||||
* component is inside a layout.
|
||||
*
|
||||
* @param width how many columns wide the component should be
|
||||
* @param height how many rows high the component should be
|
||||
* @return this component
|
||||
*/
|
||||
Derived& WithSize(int width, int height) {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_metadataDirty = true;
|
||||
return *static_cast<Derived*>(this);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <networktables/NetworkTable.h>
|
||||
#include <networktables/NetworkTableValue.h>
|
||||
#include <wpi/StringMap.h>
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardValue.h"
|
||||
|
||||
namespace frc {
|
||||
|
||||
class ShuffleboardContainer;
|
||||
|
||||
/**
|
||||
* A shim class to allow storing ShuffleboardComponents in arrays.
|
||||
*/
|
||||
class ShuffleboardComponentBase : public virtual ShuffleboardValue {
|
||||
public:
|
||||
ShuffleboardComponentBase(ShuffleboardContainer& parent,
|
||||
std::string_view title, std::string_view type = "");
|
||||
|
||||
void SetType(std::string_view type);
|
||||
|
||||
void BuildMetadata(std::shared_ptr<nt::NetworkTable> metaTable);
|
||||
|
||||
ShuffleboardContainer& GetParent();
|
||||
|
||||
const std::string& GetType() const;
|
||||
|
||||
protected:
|
||||
wpi::StringMap<nt::Value> m_properties;
|
||||
bool m_metadataDirty = true;
|
||||
int m_column = -1;
|
||||
int m_row = -1;
|
||||
int m_width = -1;
|
||||
int m_height = -1;
|
||||
|
||||
private:
|
||||
ShuffleboardContainer& m_parent;
|
||||
std::string m_type;
|
||||
|
||||
/**
|
||||
* Gets the custom properties for this component. May be null.
|
||||
*/
|
||||
const wpi::StringMap<nt::Value>& GetProperties() const;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,727 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <networktables/NetworkTableEntry.h>
|
||||
#include <networktables/NetworkTableValue.h>
|
||||
#include <wpi/SmallSet.h>
|
||||
#include <wpi/StringMap.h>
|
||||
|
||||
#include "frc/shuffleboard/BuiltInLayouts.h"
|
||||
#include "frc/shuffleboard/LayoutType.h"
|
||||
#include "frc/shuffleboard/ShuffleboardComponentBase.h"
|
||||
#include "frc/shuffleboard/ShuffleboardValue.h"
|
||||
#include "frc/shuffleboard/SuppliedValueWidget.h"
|
||||
|
||||
namespace cs {
|
||||
class VideoSource;
|
||||
} // namespace cs
|
||||
|
||||
namespace wpi {
|
||||
class Sendable;
|
||||
} // namespace wpi
|
||||
|
||||
namespace frc {
|
||||
|
||||
class ComplexWidget;
|
||||
class ShuffleboardLayout;
|
||||
class SimpleWidget;
|
||||
|
||||
/**
|
||||
* Common interface for objects that can contain shuffleboard components.
|
||||
*/
|
||||
class ShuffleboardContainer : public virtual ShuffleboardValue {
|
||||
public:
|
||||
explicit ShuffleboardContainer(std::string_view title);
|
||||
|
||||
ShuffleboardContainer(ShuffleboardContainer&& rhs) = default;
|
||||
|
||||
~ShuffleboardContainer() override = default;
|
||||
|
||||
/**
|
||||
* Gets the components that are direct children of this container.
|
||||
*/
|
||||
const std::vector<std::unique_ptr<ShuffleboardComponentBase>>& GetComponents()
|
||||
const;
|
||||
|
||||
/**
|
||||
* Gets the layout with the given type and title, creating it if it does not
|
||||
* already exist at the time this method is called.
|
||||
*
|
||||
* @param title the title of the layout
|
||||
* @param type the type of the layout, eg "List" or "Grid"
|
||||
* @return the layout
|
||||
*/
|
||||
ShuffleboardLayout& GetLayout(std::string_view title, BuiltInLayouts type);
|
||||
|
||||
/**
|
||||
* Gets the layout with the given type and title, creating it if it does not
|
||||
* already exist at the time this method is called.
|
||||
*
|
||||
* @param title the title of the layout
|
||||
* @param type the type of the layout, eg "List" or "Grid"
|
||||
* @return the layout
|
||||
*/
|
||||
ShuffleboardLayout& GetLayout(std::string_view 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
|
||||
* GetLayout(std::string_view, const LayoutType&) and the layouts in
|
||||
* 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(std::string_view, const LayoutType&)
|
||||
*/
|
||||
ShuffleboardLayout& GetLayout(std::string_view title, std::string_view 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(std::string_view title);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given sendable.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param sendable the sendable 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(std::string_view title, wpi::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(std::string_view title, const cs::VideoSource& video);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a video stream.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param cameraName the name of the streamed camera
|
||||
* @param cameraUrls the URLs with which the dashboard can access the camera
|
||||
* stream
|
||||
* @return a widget to display the camera stream
|
||||
*/
|
||||
ComplexWidget& AddCamera(std::string_view title, std::string_view cameraName,
|
||||
std::span<const std::string> cameraUrls);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given sendable.
|
||||
*
|
||||
* @param sendable the sendable to display
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this
|
||||
* container with the given title, or if the sendable's name has not been
|
||||
* specified
|
||||
*/
|
||||
ComplexWidget& Add(wpi::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.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this
|
||||
* container with the given title
|
||||
* @see AddPersistent(std::string_view, std::shared_ptr<nt::Value>)
|
||||
* Add(std::string_view title, std::shared_ptr<nt::Value> defaultValue)
|
||||
*/
|
||||
SimpleWidget& Add(std::string_view title, const nt::Value& defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this
|
||||
* container with the given title
|
||||
* @see AddPersistent(std::string_view, bool)
|
||||
* Add(std::string_view title, bool defaultValue)
|
||||
*/
|
||||
SimpleWidget& Add(std::string_view title, bool defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this
|
||||
* container with the given title
|
||||
* @see AddPersistent(std::string_view, double)
|
||||
* Add(std::string_view title, double defaultValue)
|
||||
*/
|
||||
SimpleWidget& Add(std::string_view title, double defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this
|
||||
* container with the given title
|
||||
* @see AddPersistent(std::string_view, double)
|
||||
* Add(std::string_view title, double defaultValue)
|
||||
*/
|
||||
SimpleWidget& Add(std::string_view title, float defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this
|
||||
* container with the given title
|
||||
* @see AddPersistent(std::string_view, int)
|
||||
* Add(std::string_view title, int defaultValue)
|
||||
*/
|
||||
SimpleWidget& Add(std::string_view title, int defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this
|
||||
* container with the given title
|
||||
* @see AddPersistent(std::string_view, std::string_view)
|
||||
* Add(std::string_view title, std::string_view defaultValue)
|
||||
*/
|
||||
SimpleWidget& Add(std::string_view title, std::string_view defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this
|
||||
* container with the given title
|
||||
* @see AddPersistent(std::string_view, const char*)
|
||||
* Add(std::string_view title, const char* defaultValue)
|
||||
*/
|
||||
SimpleWidget& Add(std::string_view title, const char* defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this
|
||||
* container with the given title
|
||||
* @see AddPersistent(std::string_view, std::span<const bool>)
|
||||
* Add(std::string_view title, std::span<const bool> defaultValue)
|
||||
*/
|
||||
SimpleWidget& Add(std::string_view title, std::span<const bool> defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this
|
||||
* container with the given title
|
||||
* @see AddPersistent(std::string_view, std::span<const double>)
|
||||
* Add(std::string_view title, std::span<const double> defaultValue)
|
||||
*/
|
||||
SimpleWidget& Add(std::string_view title,
|
||||
std::span<const double> defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this
|
||||
* container with the given title
|
||||
* @see AddPersistent(std::string_view, std::span<const double>)
|
||||
* Add(std::string_view title, std::span<const double> defaultValue)
|
||||
*/
|
||||
SimpleWidget& Add(std::string_view title,
|
||||
std::span<const float> defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this
|
||||
* container with the given title
|
||||
* @see AddPersistent(std::string_view, std::span<const double>)
|
||||
* Add(std::string_view title, std::span<const double> defaultValue)
|
||||
*/
|
||||
SimpleWidget& Add(std::string_view title,
|
||||
std::span<const int64_t> defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this
|
||||
* container with the given title
|
||||
* @see AddPersistent(std::string_view, std::span<const std::string>)
|
||||
* Add(std::string_view title, std::span<const std::string> defaultValue)
|
||||
*/
|
||||
SimpleWidget& Add(std::string_view title,
|
||||
std::span<const std::string> defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<std::string>& AddString(
|
||||
std::string_view title, std::function<std::string()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<double>& AddNumber(std::string_view title,
|
||||
std::function<double()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<double>& AddDouble(std::string_view title,
|
||||
std::function<double()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<float>& AddFloat(std::string_view title,
|
||||
std::function<float()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<int64_t>& AddInteger(std::string_view title,
|
||||
std::function<int64_t()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<bool>& AddBoolean(std::string_view title,
|
||||
std::function<bool()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<std::vector<std::string>>& AddStringArray(
|
||||
std::string_view title,
|
||||
std::function<std::vector<std::string>()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<std::vector<double>>& AddNumberArray(
|
||||
std::string_view title, std::function<std::vector<double>()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<std::vector<double>>& AddDoubleArray(
|
||||
std::string_view title, std::function<std::vector<double>()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<std::vector<float>>& AddFloatArray(
|
||||
std::string_view title, std::function<std::vector<float>()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<std::vector<int64_t>>& AddIntegerArray(
|
||||
std::string_view title, std::function<std::vector<int64_t>()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<std::vector<int>>& AddBooleanArray(
|
||||
std::string_view title, std::function<std::vector<int>()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<std::vector<uint8_t>>& AddRaw(
|
||||
std::string_view title, std::function<std::vector<uint8_t>()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided
|
||||
* by the value supplier. Changes made on the dashboard will not propagate to
|
||||
* the widget object, and will be overridden by values from the value
|
||||
* supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param typeString the NT type string
|
||||
* @param supplier the supplier for values
|
||||
* @return a widget to display data
|
||||
*/
|
||||
SuppliedValueWidget<std::vector<uint8_t>>& AddRaw(
|
||||
std::string_view title, std::string_view typeString,
|
||||
std::function<std::vector<uint8_t>()> supplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data.
|
||||
*
|
||||
* Unlike Add(std::string_view, std::shared_ptr<nt::Value>), the value in the
|
||||
* widget will be saved on the robot and will be used when the robot program
|
||||
* next starts rather than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @see Add(stdd::string_view, std::shared_ptr<nt::Value>)
|
||||
* Add(std::string_view title, std::shared_ptr<nt::Value> defaultValue)
|
||||
*/
|
||||
SimpleWidget& AddPersistent(std::string_view title,
|
||||
const nt::Value& defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data.
|
||||
*
|
||||
* Unlike Add(std::string_view, bool), the value in the widget will be saved
|
||||
* on the robot and will be used when the robot program next starts rather
|
||||
* than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @see Add(std::string_view, bool)
|
||||
* Add(std::string_view title, bool defaultValue)
|
||||
*/
|
||||
SimpleWidget& AddPersistent(std::string_view title, bool defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data.
|
||||
*
|
||||
* Unlike Add(std::string_view, double), the value in the widget will be saved
|
||||
* on the robot and will be used when the robot program next starts rather
|
||||
* than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @see Add(std::string_view, double)
|
||||
* Add(std::string_view title, double defaultValue)
|
||||
*/
|
||||
SimpleWidget& AddPersistent(std::string_view title, double defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data.
|
||||
*
|
||||
* Unlike Add(std::string_view, float), the value in the widget will be saved
|
||||
* on the robot and will be used when the robot program next starts rather
|
||||
* than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @see Add(std::string_view, float)
|
||||
* Add(std::string_view title, float defaultValue)
|
||||
*/
|
||||
SimpleWidget& AddPersistent(std::string_view title, float defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data.
|
||||
*
|
||||
* Unlike Add(std::string_view, int64_t), the value in the widget will be
|
||||
* saved on the robot and will be used when the robot program next starts
|
||||
* rather than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @see Add(std:string_view, int64_t)
|
||||
* Add(std::string_view title, int64_t defaultValue)
|
||||
*/
|
||||
SimpleWidget& AddPersistent(std::string_view title, int defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data.
|
||||
*
|
||||
* Unlike Add(std::string_view, std::string_view), the value in the widget
|
||||
* will be saved on the robot and will be used when the robot program next
|
||||
* starts rather than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @see Add(std::string_view, std::string_view)
|
||||
* Add(std::string_view title, std::string_view defaultValue)
|
||||
*/
|
||||
SimpleWidget& AddPersistent(std::string_view title,
|
||||
std::string_view defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data.
|
||||
*
|
||||
* Unlike Add(std::string_view, std::span<const bool>), the value in the
|
||||
* widget will be saved on the robot and will be used when the robot program
|
||||
* next starts rather than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @see Add(std::string_view, std::span<const bool>)
|
||||
* Add(std::string_view title, std::span<const bool> defaultValue)
|
||||
*/
|
||||
SimpleWidget& AddPersistent(std::string_view title,
|
||||
std::span<const bool> defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data.
|
||||
*
|
||||
* Unlike Add(std::string_view, std::span<const double>), the value in the
|
||||
* widget will be saved on the robot and will be used when the robot program
|
||||
* next starts rather than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @see Add(std::string_view, std::span<const double>)
|
||||
* Add(std::string_view title, std::span<const double> defaultValue)
|
||||
*/
|
||||
SimpleWidget& AddPersistent(std::string_view title,
|
||||
std::span<const double> defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data.
|
||||
*
|
||||
* Unlike Add(std::string_view, std::span<const float>), the value in the
|
||||
* widget will be saved on the robot and will be used when the robot program
|
||||
* next starts rather than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @see Add(std::string_view, std::span<const float>)
|
||||
* Add(std::string_view title, std::span<const float> defaultValue)
|
||||
*/
|
||||
SimpleWidget& AddPersistent(std::string_view title,
|
||||
std::span<const float> defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data.
|
||||
*
|
||||
* Unlike Add(std::string_view, std::span<const int64_t>), the value in the
|
||||
* widget will be saved on the robot and will be used when the robot program
|
||||
* next starts rather than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @see Add(std::string_view, std::span<const int64_t>)
|
||||
* Add(std::string_view title, std::span<const int64_t> defaultValue)
|
||||
*/
|
||||
SimpleWidget& AddPersistent(std::string_view title,
|
||||
std::span<const int64_t> defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data.
|
||||
*
|
||||
* Unlike Add(std::string_view, std::span<const std::string>), the value in
|
||||
* the widget will be saved on the robot and will be used when the robot
|
||||
* program next starts rather than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @see Add(std::string_view, std::span<const std::string>)
|
||||
* Add(std::string_view title, std::span<const std::string> defaultValue)
|
||||
*/
|
||||
SimpleWidget& AddPersistent(std::string_view title,
|
||||
std::span<const std::string> defaultValue);
|
||||
|
||||
void EnableIfActuator() override;
|
||||
|
||||
void DisableIfActuator() override;
|
||||
|
||||
protected:
|
||||
bool m_isLayout = false;
|
||||
|
||||
private:
|
||||
wpi::SmallSet<std::string, 32> m_usedTitles;
|
||||
std::vector<std::unique_ptr<ShuffleboardComponentBase>> m_components;
|
||||
wpi::StringMap<ShuffleboardLayout*> m_layouts;
|
||||
|
||||
/**
|
||||
* Adds title to internal set if it hasn't already.
|
||||
*
|
||||
* @return True if title isn't in use; false otherwise.
|
||||
*/
|
||||
void CheckTitle(std::string_view title);
|
||||
|
||||
friend class SimpleWidget;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
|
||||
// Make use of references returned by member functions usable
|
||||
#include "frc/shuffleboard/ComplexWidget.h"
|
||||
#include "frc/shuffleboard/ShuffleboardLayout.h"
|
||||
#include "frc/shuffleboard/SimpleWidget.h"
|
||||
|
||||
#ifndef DYNAMIC_CAMERA_SERVER
|
||||
#include "frc/shuffleboard/SendableCameraWrapper.h"
|
||||
|
||||
inline frc::ComplexWidget& frc::ShuffleboardContainer::Add(
|
||||
const cs::VideoSource& video) {
|
||||
return Add(frc::SendableCameraWrapper::Wrap(video));
|
||||
}
|
||||
|
||||
inline frc::ComplexWidget& frc::ShuffleboardContainer::Add(
|
||||
std::string_view title, const cs::VideoSource& video) {
|
||||
return Add(title, frc::SendableCameraWrapper::Wrap(video));
|
||||
}
|
||||
|
||||
inline frc::ComplexWidget& frc::ShuffleboardContainer::AddCamera(
|
||||
std::string_view title, std::string_view cameraName,
|
||||
std::span<const std::string> cameraUrls) {
|
||||
return Add(title, frc::SendableCameraWrapper::Wrap(cameraName, cameraUrls));
|
||||
}
|
||||
#endif
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace frc {
|
||||
|
||||
// Maintainer note: this enum is mirrored in WPILibJ and in Shuffleboard
|
||||
// Modifying the enum or enum strings requires a corresponding change to the
|
||||
// Java enum and the enum in Shuffleboard
|
||||
|
||||
enum ShuffleboardEventImportance { kTrivial, kLow, kNormal, kHigh, kCritical };
|
||||
|
||||
/**
|
||||
* Returns name of the given enum.
|
||||
*
|
||||
* @return Name of the given enum.
|
||||
*/
|
||||
inline std::string_view ShuffleboardEventImportanceName(
|
||||
ShuffleboardEventImportance importance) {
|
||||
switch (importance) {
|
||||
case kTrivial:
|
||||
return "TRIVIAL";
|
||||
case kLow:
|
||||
return "LOW";
|
||||
case kNormal:
|
||||
return "NORMAL";
|
||||
case kHigh:
|
||||
return "HIGH";
|
||||
case kCritical:
|
||||
return "CRITICAL";
|
||||
default:
|
||||
return "NORMAL";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardRoot.h"
|
||||
#include "frc/shuffleboard/ShuffleboardTab.h"
|
||||
|
||||
namespace frc::detail {
|
||||
|
||||
class ShuffleboardInstance final : public ShuffleboardRoot {
|
||||
public:
|
||||
explicit ShuffleboardInstance(nt::NetworkTableInstance ntInstance);
|
||||
virtual ~ShuffleboardInstance();
|
||||
|
||||
ShuffleboardInstance(ShuffleboardInstance&&) = default;
|
||||
ShuffleboardInstance& operator=(ShuffleboardInstance&&) = default;
|
||||
|
||||
frc::ShuffleboardTab& GetTab(std::string_view title) override;
|
||||
|
||||
void Update() override;
|
||||
|
||||
void EnableActuatorWidgets() override;
|
||||
|
||||
void DisableActuatorWidgets() override;
|
||||
|
||||
void SelectTab(int index) override;
|
||||
|
||||
void SelectTab(std::string_view) override;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
} // namespace frc::detail
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include <networktables/NetworkTable.h>
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardComponent.h"
|
||||
#include "frc/shuffleboard/ShuffleboardContainer.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4250)
|
||||
#endif
|
||||
|
||||
namespace frc {
|
||||
|
||||
/**
|
||||
* A layout in a Shuffleboard tab. Layouts can contain widgets and other
|
||||
* layouts.
|
||||
*/
|
||||
class ShuffleboardLayout : public ShuffleboardComponent<ShuffleboardLayout>,
|
||||
public ShuffleboardContainer {
|
||||
public:
|
||||
ShuffleboardLayout(ShuffleboardContainer& parent, std::string_view name,
|
||||
std::string_view type);
|
||||
|
||||
void BuildInto(std::shared_ptr<nt::NetworkTable> parentTable,
|
||||
std::shared_ptr<nt::NetworkTable> metaTable) override;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace frc {
|
||||
|
||||
class ShuffleboardTab;
|
||||
|
||||
/**
|
||||
* The root of the data placed in Shuffleboard. It contains the tabs, but no
|
||||
* data is placed directly in the root.
|
||||
*
|
||||
* This class is package-private to minimize API surface area.
|
||||
*/
|
||||
class ShuffleboardRoot {
|
||||
public:
|
||||
/**
|
||||
* Gets the tab with the given title, creating it if it does not already
|
||||
* exist.
|
||||
*
|
||||
* @param title the title of the tab
|
||||
* @return the tab with the given title
|
||||
*/
|
||||
virtual ShuffleboardTab& GetTab(std::string_view title) = 0;
|
||||
|
||||
/**
|
||||
* Updates all tabs.
|
||||
*/
|
||||
virtual void Update() = 0;
|
||||
|
||||
/**
|
||||
* Enables all widgets in Shuffleboard that offer user control over actuators.
|
||||
*/
|
||||
virtual void EnableActuatorWidgets() = 0;
|
||||
|
||||
/**
|
||||
* Disables all widgets in Shuffleboard that offer user control over
|
||||
* actuators.
|
||||
*/
|
||||
virtual void DisableActuatorWidgets() = 0;
|
||||
|
||||
/**
|
||||
* Selects the tab in the dashboard with the given index in the range
|
||||
* [0..n-1], where <i>n</i> is the number of tabs in the dashboard at the time
|
||||
* this method is called.
|
||||
*
|
||||
* @param index the index of the tab to select
|
||||
*/
|
||||
virtual void SelectTab(int index) = 0;
|
||||
|
||||
/**
|
||||
* Selects the tab in the dashboard with the given title.
|
||||
*
|
||||
* @param title the title of the tab to select
|
||||
*/
|
||||
virtual void SelectTab(std::string_view title) = 0;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include <networktables/NetworkTable.h>
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardContainer.h"
|
||||
|
||||
namespace frc {
|
||||
|
||||
class ShuffleboardRoot;
|
||||
|
||||
/**
|
||||
* Represents a tab in the Shuffleboard dashboard. Widgets can be added to the
|
||||
* tab with Add(Sendable), Add(std::string_view, Object), and
|
||||
* Add(String, Sendable). Widgets can also be added to layouts with
|
||||
* GetLayout(std::string_view, std::string_view); layouts can be nested
|
||||
* arbitrarily deep (note that too many levels may make deeper components
|
||||
* unusable).
|
||||
*/
|
||||
class ShuffleboardTab final : public ShuffleboardContainer {
|
||||
public:
|
||||
ShuffleboardTab(ShuffleboardRoot& root, std::string_view title);
|
||||
|
||||
ShuffleboardRoot& GetRoot();
|
||||
|
||||
void BuildInto(std::shared_ptr<nt::NetworkTable> parentTable,
|
||||
std::shared_ptr<nt::NetworkTable> metaTable) override;
|
||||
|
||||
private:
|
||||
ShuffleboardRoot& m_root;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,67 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <networktables/NetworkTable.h>
|
||||
|
||||
namespace frc {
|
||||
|
||||
class ShuffleboardValue {
|
||||
public:
|
||||
explicit ShuffleboardValue(std::string_view title) : m_title(title) {}
|
||||
|
||||
virtual ~ShuffleboardValue() = default;
|
||||
|
||||
ShuffleboardValue(const ShuffleboardValue&) = delete;
|
||||
ShuffleboardValue& operator=(const ShuffleboardValue&) = delete;
|
||||
|
||||
/**
|
||||
* Gets the title of this Shuffleboard value.
|
||||
*/
|
||||
const std::string& GetTitle() const { return m_title; }
|
||||
|
||||
/**
|
||||
* Builds the entries for this value.
|
||||
*
|
||||
* @param parentTable The table containing all the data for the parent. Values
|
||||
* that require a complex entry or table structure should
|
||||
* call {@code parentTable.getSubtable(getTitle())} to get
|
||||
* the table to put data into. Values that only use a
|
||||
* single entry should call
|
||||
* {@code parentTable.getEntry(getTitle())} to get that
|
||||
* entry.
|
||||
* @param metaTable The table containing all the metadata for this value and
|
||||
* its sub-values
|
||||
*/
|
||||
virtual void BuildInto(std::shared_ptr<nt::NetworkTable> parentTable,
|
||||
std::shared_ptr<nt::NetworkTable> metaTable) = 0;
|
||||
|
||||
/**
|
||||
* Enables user control of this widget in the Shuffleboard application.
|
||||
*
|
||||
* This method is package-private to prevent users from enabling control
|
||||
* themselves. Has no effect if the sendable is not marked as an actuator with
|
||||
* SendableBuilder::SetActuator().
|
||||
*/
|
||||
virtual void EnableIfActuator() {}
|
||||
|
||||
/**
|
||||
* Disables user control of this widget in the Shuffleboard application.
|
||||
*
|
||||
* This method is package-private to prevent users from enabling control
|
||||
* themselves. Has no effect if the sendable is not marked as an actuator with
|
||||
* SendableBuilder::SetActuator().
|
||||
*/
|
||||
virtual void DisableIfActuator() {}
|
||||
|
||||
private:
|
||||
std::string m_title;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "frc/shuffleboard/BuiltInWidgets.h"
|
||||
#include "frc/shuffleboard/ShuffleboardComponent.h"
|
||||
#include "frc/shuffleboard/WidgetType.h"
|
||||
|
||||
namespace frc {
|
||||
|
||||
class ShuffleboardContainer;
|
||||
|
||||
namespace detail {
|
||||
const char* GetStringForWidgetType(BuiltInWidgets type);
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Abstract superclass for widgets.
|
||||
*
|
||||
* <p>This class is package-private to minimize API surface area.
|
||||
*
|
||||
* @tparam Derived the self type
|
||||
*/
|
||||
template <typename Derived>
|
||||
class ShuffleboardWidget : public ShuffleboardComponent<Derived> {
|
||||
public:
|
||||
ShuffleboardWidget(ShuffleboardContainer& parent, std::string_view title)
|
||||
: ShuffleboardValue(title),
|
||||
ShuffleboardComponent<Derived>(parent, title) {}
|
||||
|
||||
/**
|
||||
* Sets the type of widget used to display the data. If not set, the default
|
||||
* widget type will be used.
|
||||
*
|
||||
* @param widgetType the type of the widget used to display the data
|
||||
* @return this widget object
|
||||
* @see BuiltInWidgets
|
||||
*/
|
||||
Derived& WithWidget(BuiltInWidgets widgetType) {
|
||||
return WithWidget(detail::GetStringForWidgetType(widgetType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of widget used to display the data. If not set, the default
|
||||
* widget type will be used.
|
||||
*
|
||||
* @param widgetType the type of the widget used to display the data
|
||||
* @return this widget object
|
||||
*/
|
||||
Derived& WithWidget(const WidgetType& widgetType) {
|
||||
return WithWidget(widgetType.GetWidgetName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 third-party plugin). To use a widget that is built into
|
||||
* Shuffleboard, use WithWidget(WidgetType) and BuiltInWidgets.
|
||||
*
|
||||
* @param widgetType the type of the widget used to display the data
|
||||
* @return this widget object
|
||||
*/
|
||||
Derived& WithWidget(std::string_view widgetType) {
|
||||
this->SetType(widgetType);
|
||||
return *static_cast<Derived*>(this);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <networktables/GenericEntry.h>
|
||||
#include <networktables/NetworkTable.h>
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardWidget.h"
|
||||
|
||||
namespace frc {
|
||||
|
||||
class ShuffleboardContainer;
|
||||
|
||||
/**
|
||||
* A Shuffleboard widget that handles a single data point such as a number or
|
||||
* string.
|
||||
*/
|
||||
class SimpleWidget final : public ShuffleboardWidget<SimpleWidget> {
|
||||
public:
|
||||
SimpleWidget(ShuffleboardContainer& parent, std::string_view title);
|
||||
|
||||
/**
|
||||
* Gets the NetworkTable entry that contains the data for this widget.
|
||||
* The widget owns the entry; the returned pointer's lifetime is the same as
|
||||
* that of the widget.
|
||||
*/
|
||||
nt::GenericEntry* GetEntry();
|
||||
|
||||
/**
|
||||
* Gets the NetworkTable entry that contains the data for this widget.
|
||||
* The widget owns the entry; the returned pointer's lifetime is the same as
|
||||
* that of the widget.
|
||||
*
|
||||
* @param typeString NT type string
|
||||
*/
|
||||
nt::GenericEntry* GetEntry(std::string_view typeString);
|
||||
|
||||
void BuildInto(std::shared_ptr<nt::NetworkTable> parentTable,
|
||||
std::shared_ptr<nt::NetworkTable> metaTable) override;
|
||||
|
||||
private:
|
||||
nt::GenericEntry m_entry;
|
||||
std::string m_typeString;
|
||||
|
||||
void ForceGenerate();
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <networktables/BooleanTopic.h>
|
||||
#include <networktables/GenericEntry.h>
|
||||
#include <networktables/NetworkTable.h>
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardComponent.h"
|
||||
#include "frc/shuffleboard/ShuffleboardComponentBase.h"
|
||||
#include "frc/shuffleboard/ShuffleboardWidget.h"
|
||||
|
||||
namespace frc {
|
||||
class ShuffleboardContainer;
|
||||
|
||||
template <typename T>
|
||||
class SuppliedValueWidget : public ShuffleboardWidget<SuppliedValueWidget<T>> {
|
||||
public:
|
||||
SuppliedValueWidget(ShuffleboardContainer& parent, std::string_view title,
|
||||
std::string_view typeString, std::function<T()> supplier,
|
||||
std::function<void(nt::GenericPublisher&, T)> setter)
|
||||
: ShuffleboardValue(title),
|
||||
ShuffleboardWidget<SuppliedValueWidget<T>>(parent, title),
|
||||
m_typeString(typeString),
|
||||
m_supplier(supplier),
|
||||
m_setter(setter) {}
|
||||
|
||||
void BuildInto(std::shared_ptr<nt::NetworkTable> parentTable,
|
||||
std::shared_ptr<nt::NetworkTable> metaTable) override {
|
||||
this->BuildMetadata(metaTable);
|
||||
if (!m_controllablePub) {
|
||||
m_controllablePub =
|
||||
nt::BooleanTopic{metaTable->GetTopic("Controllable")}.Publish();
|
||||
m_controllablePub.Set(false);
|
||||
}
|
||||
|
||||
if (!m_entry) {
|
||||
m_entry =
|
||||
parentTable->GetTopic(this->GetTitle()).GenericPublish(m_typeString);
|
||||
}
|
||||
m_setter(m_entry, m_supplier());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_typeString;
|
||||
std::function<T()> m_supplier;
|
||||
std::function<void(nt::GenericPublisher&, T)> m_setter;
|
||||
nt::BooleanPublisher m_controllablePub;
|
||||
nt::GenericPublisher m_entry;
|
||||
};
|
||||
} // namespace frc
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
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 constexpr WidgetType(const char* widgetName)
|
||||
: m_widgetName(widgetName) {}
|
||||
~WidgetType() = default;
|
||||
|
||||
/**
|
||||
* Gets the string type of the widget as defined by that widget in
|
||||
* Shuffleboard.
|
||||
*/
|
||||
std::string_view GetWidgetName() const;
|
||||
|
||||
private:
|
||||
const char* m_widgetName;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "shuffleboard/MockActuatorSendable.h"
|
||||
|
||||
#include <wpi/sendable/SendableBuilder.h>
|
||||
#include <wpi/sendable/SendableRegistry.h>
|
||||
|
||||
MockActuatorSendable::MockActuatorSendable(std::string_view name) {
|
||||
wpi::SendableRegistry::Add(this, name);
|
||||
}
|
||||
|
||||
void MockActuatorSendable::InitSendable(wpi::SendableBuilder& builder) {
|
||||
builder.SetActuator(true);
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardInstance.h" // NOLINT(build/include_order)
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <networktables/NetworkTableListener.h>
|
||||
#include <networktables/StringTopic.h>
|
||||
|
||||
#include "shuffleboard/MockActuatorSendable.h"
|
||||
|
||||
class NTWrapper {
|
||||
public:
|
||||
NTWrapper() { inst = nt::NetworkTableInstance::Create(); }
|
||||
|
||||
~NTWrapper() { nt::NetworkTableInstance::Destroy(inst); }
|
||||
|
||||
nt::NetworkTableInstance inst;
|
||||
};
|
||||
|
||||
TEST(ShuffleboardInstanceTest, PathFluent) {
|
||||
NTWrapper ntInst;
|
||||
frc::detail::ShuffleboardInstance shuffleboardInst{ntInst.inst};
|
||||
|
||||
auto entry = shuffleboardInst.GetTab("Tab Title")
|
||||
.GetLayout("List", "List Layout")
|
||||
.Add("Data", "string")
|
||||
.WithWidget("Text View")
|
||||
.GetEntry();
|
||||
|
||||
EXPECT_EQ("string", entry->GetString("")) << "Wrong entry value";
|
||||
EXPECT_EQ("/Shuffleboard/Tab Title/List/Data", entry->GetTopic().GetName())
|
||||
<< "Entry path generated incorrectly";
|
||||
}
|
||||
|
||||
TEST(ShuffleboardInstanceTest, NestedLayoutsFluent) {
|
||||
NTWrapper ntInst;
|
||||
frc::detail::ShuffleboardInstance shuffleboardInst{ntInst.inst};
|
||||
|
||||
auto entry = shuffleboardInst.GetTab("Tab")
|
||||
.GetLayout("First", "List")
|
||||
.GetLayout("Second", "List")
|
||||
.GetLayout("Third", "List")
|
||||
.GetLayout("Fourth", "List")
|
||||
.Add("Value", "string")
|
||||
.GetEntry();
|
||||
|
||||
EXPECT_EQ("string", entry->GetString("")) << "Wrong entry value";
|
||||
EXPECT_EQ("/Shuffleboard/Tab/First/Second/Third/Fourth/Value",
|
||||
entry->GetTopic().GetName())
|
||||
<< "Entry path generated incorrectly";
|
||||
}
|
||||
|
||||
TEST(ShuffleboardInstanceTest, NestedLayoutsOop) {
|
||||
NTWrapper ntInst;
|
||||
frc::detail::ShuffleboardInstance shuffleboardInst{ntInst.inst};
|
||||
|
||||
frc::ShuffleboardTab& tab = shuffleboardInst.GetTab("Tab");
|
||||
frc::ShuffleboardLayout& first = tab.GetLayout("First", "List");
|
||||
frc::ShuffleboardLayout& second = first.GetLayout("Second", "List");
|
||||
frc::ShuffleboardLayout& third = second.GetLayout("Third", "List");
|
||||
frc::ShuffleboardLayout& fourth = third.GetLayout("Fourth", "List");
|
||||
frc::SimpleWidget& widget = fourth.Add("Value", "string");
|
||||
auto entry = widget.GetEntry();
|
||||
|
||||
EXPECT_EQ("string", entry->GetString("")) << "Wrong entry value";
|
||||
EXPECT_EQ("/Shuffleboard/Tab/First/Second/Third/Fourth/Value",
|
||||
entry->GetTopic().GetName())
|
||||
<< "Entry path generated incorrectly";
|
||||
}
|
||||
|
||||
TEST(ShuffleboardInstanceTest, LayoutTypeIsSet) {
|
||||
NTWrapper ntInst;
|
||||
frc::detail::ShuffleboardInstance shuffleboardInst{ntInst.inst};
|
||||
|
||||
std::string_view layoutType = "Type";
|
||||
shuffleboardInst.GetTab("Tab").GetLayout("Title", layoutType);
|
||||
shuffleboardInst.Update();
|
||||
auto entry = ntInst.inst.GetEntry(
|
||||
"/Shuffleboard/.metadata/Tab/Title/PreferredComponent");
|
||||
EXPECT_EQ(layoutType, entry.GetString("Not Set")) << "Layout type not set";
|
||||
}
|
||||
|
||||
TEST(ShuffleboardInstanceTest, NestedActuatorWidgetsAreDisabled) {
|
||||
NTWrapper ntInst;
|
||||
frc::detail::ShuffleboardInstance shuffleboardInst{ntInst.inst};
|
||||
|
||||
MockActuatorSendable sendable("Actuator");
|
||||
shuffleboardInst.GetTab("Tab").GetLayout("Title", "Layout").Add(sendable);
|
||||
auto controllableEntry =
|
||||
ntInst.inst.GetEntry("/Shuffleboard/Tab/Title/Actuator/.controllable");
|
||||
shuffleboardInst.Update();
|
||||
|
||||
// Note: we use the unsafe `GetBoolean()` method because if the value is NOT
|
||||
// a boolean, or if it is not present, then something has clearly gone very,
|
||||
// very wrong
|
||||
bool controllable = controllableEntry.GetValue().GetBoolean();
|
||||
// Sanity check
|
||||
EXPECT_TRUE(controllable)
|
||||
<< "The nested actuator widget should be enabled by default";
|
||||
shuffleboardInst.DisableActuatorWidgets();
|
||||
controllable = controllableEntry.GetValue().GetBoolean();
|
||||
EXPECT_FALSE(controllable)
|
||||
<< "The nested actuator widget should have been disabled";
|
||||
}
|
||||
|
||||
TEST(ShuffleboardInstanceTest, DuplicateSelectTabs) {
|
||||
NTWrapper ntInst;
|
||||
frc::detail::ShuffleboardInstance shuffleboardInst{ntInst.inst};
|
||||
std::atomic_int counter = 0;
|
||||
auto subscriber =
|
||||
ntInst.inst.GetStringTopic("/Shuffleboard/.metadata/Selected")
|
||||
.Subscribe("", {.keepDuplicates = true});
|
||||
ntInst.inst.AddListener(
|
||||
subscriber, nt::EventFlags::kValueAll | nt::EventFlags::kImmediate,
|
||||
[&counter](auto& event) { counter++; });
|
||||
|
||||
// There shouldn't be anything there
|
||||
EXPECT_EQ(0, counter);
|
||||
|
||||
shuffleboardInst.SelectTab("tab1");
|
||||
shuffleboardInst.SelectTab("tab1");
|
||||
EXPECT_TRUE(ntInst.inst.WaitForListenerQueue(1.0))
|
||||
<< "Listener queue timed out!";
|
||||
EXPECT_EQ(2, counter);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "frc/shuffleboard/Shuffleboard.h"
|
||||
|
||||
TEST(ShuffleboardTest, TabObjectsCached) {
|
||||
auto& tab1 = frc::Shuffleboard::GetTab("testTabObjectsCached");
|
||||
auto& tab2 = frc::Shuffleboard::GetTab("testTabObjectsCached");
|
||||
EXPECT_EQ(&tab1, &tab2) << "Tab objects were not cached";
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <networktables/NetworkTableEntry.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
|
||||
#include "frc/shuffleboard/ShuffleboardInstance.h"
|
||||
#include "frc/shuffleboard/ShuffleboardTab.h"
|
||||
|
||||
using namespace frc;
|
||||
|
||||
class NTWrapper {
|
||||
public:
|
||||
NTWrapper() { inst = nt::NetworkTableInstance::Create(); }
|
||||
|
||||
~NTWrapper() { nt::NetworkTableInstance::Destroy(inst); }
|
||||
|
||||
nt::NetworkTableInstance inst;
|
||||
};
|
||||
|
||||
class SuppliedValueWidgetTest : public testing::Test {
|
||||
protected:
|
||||
NTWrapper m_ntInst;
|
||||
frc::detail::ShuffleboardInstance m_shuffleboardInst{m_ntInst.inst};
|
||||
frc::ShuffleboardTab* m_tab = &(m_shuffleboardInst.GetTab("Tab"));
|
||||
};
|
||||
|
||||
TEST_F(SuppliedValueWidgetTest, AddString) {
|
||||
std::string str = "foo";
|
||||
m_tab->AddString("String", [&str]() { return str; });
|
||||
auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/String");
|
||||
|
||||
m_shuffleboardInst.Update();
|
||||
EXPECT_EQ("foo", entry.GetValue().GetString());
|
||||
}
|
||||
|
||||
TEST_F(SuppliedValueWidgetTest, AddNumber) {
|
||||
int num = 0;
|
||||
m_tab->AddNumber("Num", [&num]() { return ++num; });
|
||||
auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/Num");
|
||||
|
||||
m_shuffleboardInst.Update();
|
||||
EXPECT_FLOAT_EQ(1.0, entry.GetValue().GetDouble());
|
||||
}
|
||||
|
||||
TEST_F(SuppliedValueWidgetTest, AddBoolean) {
|
||||
bool value = true;
|
||||
m_tab->AddBoolean("Bool", [&value]() { return value; });
|
||||
auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/Bool");
|
||||
|
||||
m_shuffleboardInst.Update();
|
||||
EXPECT_EQ(true, entry.GetValue().GetBoolean());
|
||||
}
|
||||
|
||||
TEST_F(SuppliedValueWidgetTest, AddStringArray) {
|
||||
std::vector<std::string> strings = {"foo", "bar"};
|
||||
m_tab->AddStringArray("Strings", [&strings]() { return strings; });
|
||||
auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/Strings");
|
||||
|
||||
m_shuffleboardInst.Update();
|
||||
auto actual = entry.GetValue().GetStringArray();
|
||||
|
||||
EXPECT_EQ(strings.size(), actual.size());
|
||||
for (size_t i = 0; i < strings.size(); i++) {
|
||||
EXPECT_EQ(strings[i], actual[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SuppliedValueWidgetTest, AddNumberArray) {
|
||||
std::vector<double> nums = {0, 1, 2, 3};
|
||||
m_tab->AddNumberArray("Numbers", [&nums]() { return nums; });
|
||||
auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/Numbers");
|
||||
|
||||
m_shuffleboardInst.Update();
|
||||
auto actual = entry.GetValue().GetDoubleArray();
|
||||
|
||||
EXPECT_EQ(nums.size(), actual.size());
|
||||
for (size_t i = 0; i < nums.size(); i++) {
|
||||
EXPECT_FLOAT_EQ(nums[i], actual[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SuppliedValueWidgetTest, AddBooleanArray) {
|
||||
std::vector<int> bools = {true, false};
|
||||
m_tab->AddBooleanArray("Booleans", [&bools]() { return bools; });
|
||||
auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/Booleans");
|
||||
|
||||
m_shuffleboardInst.Update();
|
||||
auto actual = entry.GetValue().GetBooleanArray();
|
||||
|
||||
EXPECT_EQ(bools.size(), actual.size());
|
||||
for (size_t i = 0; i < bools.size(); i++) {
|
||||
EXPECT_FLOAT_EQ(bools[i], actual[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SuppliedValueWidgetTest, AddRaw) {
|
||||
std::vector<uint8_t> bytes = {1, 2, 3};
|
||||
m_tab->AddRaw("Raw", [&bytes]() { return bytes; });
|
||||
auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/Raw");
|
||||
|
||||
m_shuffleboardInst.Update();
|
||||
auto actual = entry.GetValue().GetRaw();
|
||||
EXPECT_EQ(bytes, std::vector<uint8_t>(actual.begin(), actual.end()));
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <wpi/sendable/Sendable.h>
|
||||
|
||||
/**
|
||||
* A mock sendable that marks itself as an actuator.
|
||||
*/
|
||||
class MockActuatorSendable : public wpi::Sendable {
|
||||
public:
|
||||
explicit MockActuatorSendable(std::string_view name);
|
||||
|
||||
void InitSendable(wpi::SendableBuilder& builder) override;
|
||||
};
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "RobotContainer.h"
|
||||
|
||||
#include <frc/shuffleboard/Shuffleboard.h>
|
||||
#include <frc/smartdashboard/SmartDashboard.h>
|
||||
|
||||
RobotContainer::RobotContainer() {
|
||||
// Initialize all of your commands and subsystems here
|
||||
@@ -15,36 +15,10 @@ RobotContainer::RobotContainer() {
|
||||
m_chooser.AddOption("Complex Auto", m_complexAuto.get());
|
||||
|
||||
// Put the chooser on the dashboard
|
||||
frc::Shuffleboard::GetTab("Autonomous").Add(m_chooser);
|
||||
frc::SmartDashboard::PutData("Autonomous", &m_chooser);
|
||||
// Put subsystems to dashboard.
|
||||
frc::Shuffleboard::GetTab("Drivetrain").Add(m_drive);
|
||||
frc::Shuffleboard::GetTab("HatchSubsystem").Add(m_hatch);
|
||||
|
||||
// Log Shuffleboard events for command initialize, execute, finish, interrupt
|
||||
frc2::CommandScheduler::GetInstance().OnCommandInitialize(
|
||||
[](const frc2::Command& command) {
|
||||
frc::Shuffleboard::AddEventMarker(
|
||||
"Command initialized", command.GetName(),
|
||||
frc::ShuffleboardEventImportance::kNormal);
|
||||
});
|
||||
frc2::CommandScheduler::GetInstance().OnCommandExecute(
|
||||
[](const frc2::Command& command) {
|
||||
frc::Shuffleboard::AddEventMarker(
|
||||
"Command executed", command.GetName(),
|
||||
frc::ShuffleboardEventImportance::kNormal);
|
||||
});
|
||||
frc2::CommandScheduler::GetInstance().OnCommandFinish(
|
||||
[](const frc2::Command& command) {
|
||||
frc::Shuffleboard::AddEventMarker(
|
||||
"Command finished", command.GetName(),
|
||||
frc::ShuffleboardEventImportance::kNormal);
|
||||
});
|
||||
frc2::CommandScheduler::GetInstance().OnCommandInterrupt(
|
||||
[](const frc2::Command& command) {
|
||||
frc::Shuffleboard::AddEventMarker(
|
||||
"Command interrupted", command.GetName(),
|
||||
frc::ShuffleboardEventImportance::kNormal);
|
||||
});
|
||||
frc::SmartDashboard::PutData("Drivetrain", &m_drive);
|
||||
frc::SmartDashboard::PutData("HatchSubsystem", &m_hatch);
|
||||
|
||||
// Configure the button bindings
|
||||
ConfigureButtonBindings();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "RobotContainer.h"
|
||||
|
||||
#include <frc/shuffleboard/Shuffleboard.h>
|
||||
#include <frc/smartdashboard/SmartDashboard.h>
|
||||
#include <frc2/command/button/JoystickButton.h>
|
||||
|
||||
#include "commands/DefaultDrive.h"
|
||||
@@ -20,36 +20,10 @@ RobotContainer::RobotContainer() {
|
||||
m_chooser.AddOption("Complex Auto", &m_complexAuto);
|
||||
|
||||
// Put the chooser on the dashboard
|
||||
frc::Shuffleboard::GetTab("Autonomous").Add(m_chooser);
|
||||
frc::SmartDashboard::PutData("Autonomous", &m_chooser);
|
||||
// Put subsystems to dashboard.
|
||||
frc::Shuffleboard::GetTab("Drivetrain").Add(m_drive);
|
||||
frc::Shuffleboard::GetTab("HatchSubsystem").Add(m_hatch);
|
||||
|
||||
// Log Shuffleboard events for command initialize, execute, finish, interrupt
|
||||
frc2::CommandScheduler::GetInstance().OnCommandInitialize(
|
||||
[](const frc2::Command& command) {
|
||||
frc::Shuffleboard::AddEventMarker(
|
||||
"Command initialized", command.GetName(),
|
||||
frc::ShuffleboardEventImportance::kNormal);
|
||||
});
|
||||
frc2::CommandScheduler::GetInstance().OnCommandExecute(
|
||||
[](const frc2::Command& command) {
|
||||
frc::Shuffleboard::AddEventMarker(
|
||||
"Command executed", command.GetName(),
|
||||
frc::ShuffleboardEventImportance::kNormal);
|
||||
});
|
||||
frc2::CommandScheduler::GetInstance().OnCommandFinish(
|
||||
[](const frc2::Command& command) {
|
||||
frc::Shuffleboard::AddEventMarker(
|
||||
"Command finished", command.GetName(),
|
||||
frc::ShuffleboardEventImportance::kNormal);
|
||||
});
|
||||
frc2::CommandScheduler::GetInstance().OnCommandInterrupt(
|
||||
[](const frc2::Command& command) {
|
||||
frc::Shuffleboard::AddEventMarker(
|
||||
"Command interrupted", command.GetName(),
|
||||
frc::ShuffleboardEventImportance::kNormal);
|
||||
});
|
||||
frc::SmartDashboard::PutData("Drivetrain", &m_drive);
|
||||
frc::SmartDashboard::PutData("HatchSubsystem", &m_hatch);
|
||||
|
||||
// Configure the button bindings
|
||||
ConfigureButtonBindings();
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <frc/controller/PIDController.h>
|
||||
#include <frc/geometry/Translation2d.h>
|
||||
#include <frc/shuffleboard/Shuffleboard.h>
|
||||
#include <frc/trajectory/Trajectory.h>
|
||||
#include <frc/trajectory/TrajectoryGenerator.h>
|
||||
#include <frc/trajectory/constraint/MecanumDriveKinematicsConstraint.h>
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <frc/AnalogPotentiometer.h>
|
||||
#include <frc/Encoder.h>
|
||||
#include <frc/Joystick.h>
|
||||
#include <frc/TimedRobot.h>
|
||||
#include <frc/drive/DifferentialDrive.h>
|
||||
#include <frc/motorcontrol/PWMSparkMax.h>
|
||||
#include <frc/shuffleboard/Shuffleboard.h>
|
||||
#include <frc/shuffleboard/ShuffleboardLayout.h>
|
||||
#include <frc/shuffleboard/ShuffleboardTab.h>
|
||||
#include <networktables/GenericEntry.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
|
||||
/**
|
||||
* This sample program provides an example for ShuffleBoard, an alternative
|
||||
* to SmartDashboard for displaying values and properties of different robot
|
||||
* parts.
|
||||
*
|
||||
* ShuffleBoard can use pre-programmed widgets to display various values, such
|
||||
* as Boolean Boxes, Sliders, Graphs, and more. In addition, they can display
|
||||
* things in various Tabs.
|
||||
*
|
||||
* For more information on how to create personal layouts and more in
|
||||
* ShuffleBoard, feel free to reference the official FIRST WPILib documentation
|
||||
* online.
|
||||
*/
|
||||
class Robot : public frc::TimedRobot {
|
||||
public:
|
||||
Robot() {
|
||||
wpi::SendableRegistry::AddChild(&m_robotDrive, &m_left);
|
||||
wpi::SendableRegistry::AddChild(&m_robotDrive, &m_right);
|
||||
|
||||
// Add a widget titled 'Max Speed' with a number slider.
|
||||
m_maxSpeed = frc::Shuffleboard::GetTab("Configuration")
|
||||
.Add("Max Speed", 1)
|
||||
.WithWidget("Number Slider")
|
||||
.GetEntry();
|
||||
|
||||
// Create a 'DriveBase' tab and add the drivetrain object to it.
|
||||
frc::ShuffleboardTab& driveBaseTab = frc::Shuffleboard::GetTab("DriveBase");
|
||||
driveBaseTab.Add("TankDrive", m_robotDrive);
|
||||
|
||||
// Put encoders in a list layout.
|
||||
frc::ShuffleboardLayout& encoders =
|
||||
driveBaseTab.GetLayout("Encoders", frc::BuiltInLayouts::kList)
|
||||
.WithPosition(0, 0)
|
||||
.WithSize(2, 2);
|
||||
encoders.Add("Left Encoder", m_leftEncoder);
|
||||
encoders.Add("Right Encoder", m_rightEncoder);
|
||||
|
||||
// Create a 'Elevator' tab and add the potentiometer and elevator motor to
|
||||
// it.
|
||||
frc::ShuffleboardTab& elevatorTab = frc::Shuffleboard::GetTab("Elevator");
|
||||
elevatorTab.Add("Motor", m_elevatorMotor);
|
||||
elevatorTab.Add("Potentiometer", m_ElevatorPot);
|
||||
}
|
||||
|
||||
void AutonomousInit() override {
|
||||
// Update the Max Output for the drivetrain.
|
||||
m_robotDrive.SetMaxOutput(m_maxSpeed->GetDouble(1.0));
|
||||
}
|
||||
|
||||
private:
|
||||
frc::PWMSparkMax m_left{0};
|
||||
frc::PWMSparkMax m_right{1};
|
||||
frc::PWMSparkMax m_elevatorMotor{2};
|
||||
|
||||
frc::DifferentialDrive m_robotDrive{
|
||||
[&](double output) { m_left.Set(output); },
|
||||
[&](double output) { m_right.Set(output); }};
|
||||
|
||||
frc::Joystick m_stick{0};
|
||||
|
||||
frc::Encoder m_leftEncoder{0, 1};
|
||||
frc::Encoder m_rightEncoder{2, 3};
|
||||
frc::AnalogPotentiometer m_ElevatorPot{0};
|
||||
|
||||
nt::GenericEntry* m_maxSpeed;
|
||||
};
|
||||
|
||||
#ifndef RUNNING_FRC_TESTS
|
||||
int main() {
|
||||
return frc::StartRobot<Robot>();
|
||||
}
|
||||
#endif
|
||||
@@ -4,41 +4,35 @@
|
||||
|
||||
#include "Robot.h"
|
||||
|
||||
#include <frc/shuffleboard/Shuffleboard.h>
|
||||
#include <frc/smartdashboard/SmartDashboard.h>
|
||||
#include <units/pressure.h>
|
||||
|
||||
Robot::Robot() {
|
||||
// Publish elements to shuffleboard.
|
||||
frc::ShuffleboardTab& tab = frc::Shuffleboard::GetTab("Pneumatics");
|
||||
tab.Add("Single Solenoid", m_solenoid);
|
||||
tab.Add("Double Solenoid", m_doubleSolenoid);
|
||||
tab.Add("Compressor", m_compressor);
|
||||
|
||||
// Also publish some raw data
|
||||
tab.AddDouble("PH Pressure [PSI]", [&] {
|
||||
// Get the pressure (in PSI) from the analog sensor connected to the PH.
|
||||
// This function is supported only on the PH!
|
||||
// On a PCM, this function will return 0.
|
||||
units::pounds_per_square_inch_t pressure = m_compressor.GetPressure();
|
||||
return pressure.value();
|
||||
});
|
||||
tab.AddDouble("Compressor Current", [&] {
|
||||
// Get compressor current draw.
|
||||
units::ampere_t compressorCurrent = m_compressor.GetCurrent();
|
||||
return compressorCurrent.value();
|
||||
});
|
||||
tab.AddBoolean("Compressor Active", [&] {
|
||||
// Get whether the compressor is active.
|
||||
return m_compressor.IsEnabled();
|
||||
});
|
||||
tab.AddBoolean("Pressure Switch", [&] {
|
||||
// Get the digital pressure switch connected to the PCM/PH.
|
||||
// The switch is open when the pressure is over ~120 PSI.
|
||||
return m_compressor.GetPressureSwitchValue();
|
||||
});
|
||||
frc::SmartDashboard::PutData("Single Solenoid", &m_solenoid);
|
||||
frc::SmartDashboard::PutData("Double Solenoid", &m_doubleSolenoid);
|
||||
frc::SmartDashboard::PutData("Compressor", &m_compressor);
|
||||
}
|
||||
|
||||
void Robot::TeleopPeriodic() {
|
||||
// Publish some raw data
|
||||
|
||||
// Get the pressure (in PSI) from the analog sensor connected to the PH.
|
||||
// This function is supported only on the PH!
|
||||
// On a PCM, this function will return 0.
|
||||
frc::SmartDashboard::PutNumber("PH Pressure [PSI]",
|
||||
m_compressor.GetPressure().value());
|
||||
// Get compressor current draw.
|
||||
frc::SmartDashboard::PutNumber("Compressor Current",
|
||||
m_compressor.GetCurrent().value());
|
||||
// Get whether the compressor is active.
|
||||
frc::SmartDashboard::PutBoolean("Compressor Active",
|
||||
m_compressor.IsEnabled());
|
||||
// Get the digital pressure switch connected to the PCM/PH.
|
||||
// The switch is open when the pressure is over ~120 PSI.
|
||||
frc::SmartDashboard::PutBoolean("Pressure Switch",
|
||||
m_compressor.GetPressureSwitchValue());
|
||||
|
||||
/*
|
||||
* The output of GetRawButton is true/false depending on whether
|
||||
* the button is pressed; Set takes a boolean for whether
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <frc/controller/PIDController.h>
|
||||
#include <frc/geometry/Translation2d.h>
|
||||
#include <frc/shuffleboard/Shuffleboard.h>
|
||||
#include <frc/trajectory/Trajectory.h>
|
||||
#include <frc/trajectory/TrajectoryGenerator.h>
|
||||
#include <frc2/command/Commands.h>
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
|
||||
#include "Robot.h"
|
||||
|
||||
#include <frc/shuffleboard/Shuffleboard.h>
|
||||
#include <frc/smartdashboard/SmartDashboard.h>
|
||||
#include <units/length.h>
|
||||
|
||||
Robot::Robot() {
|
||||
// Add the ultrasonic on the "Sensors" tab of the dashboard
|
||||
// Data will update automatically
|
||||
frc::Shuffleboard::GetTab("Sensors").Add(m_rangeFinder);
|
||||
frc::SmartDashboard::PutData("Sensors", &m_rangeFinder);
|
||||
}
|
||||
|
||||
void Robot::TeleopPeriodic() {
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
"tags": [
|
||||
"Hardware",
|
||||
"Joystick",
|
||||
"Shuffleboard",
|
||||
"Pneumatics"
|
||||
],
|
||||
"foldername": "Solenoid",
|
||||
@@ -119,8 +118,7 @@
|
||||
"tags": [
|
||||
"Hardware",
|
||||
"Ultrasonic",
|
||||
"SmartDashboard",
|
||||
"Shuffleboard"
|
||||
"SmartDashboard"
|
||||
],
|
||||
"foldername": "Ultrasonic",
|
||||
"gradlebase": "cpp",
|
||||
@@ -318,21 +316,6 @@
|
||||
"gradlebase": "c",
|
||||
"commandversion": 2
|
||||
},
|
||||
{
|
||||
"name": "Shuffleboard",
|
||||
"description": "Present various data via the Shuffleboard API.",
|
||||
"tags": [
|
||||
"Basic Robot",
|
||||
"Differential Drive",
|
||||
"Elevator",
|
||||
"Analog",
|
||||
"Encoder",
|
||||
"Shuffleboard"
|
||||
],
|
||||
"foldername": "ShuffleBoard",
|
||||
"gradlebase": "cpp",
|
||||
"commandversion": 2
|
||||
},
|
||||
{
|
||||
"name": "'Traditional' Hatchbot",
|
||||
"description": "A fully-functional command-based hatchbot for the 2019 game, written in the 'traditional' style, i.e. commands are given their own classes.",
|
||||
@@ -341,7 +324,6 @@
|
||||
"Command-based",
|
||||
"Differential Drive",
|
||||
"Encoder",
|
||||
"Shuffleboard",
|
||||
"Sendable",
|
||||
"DataLog",
|
||||
"Pneumatics",
|
||||
@@ -359,7 +341,6 @@
|
||||
"Command-based",
|
||||
"Differential Drive",
|
||||
"Encoder",
|
||||
"Shuffleboard",
|
||||
"Sendable",
|
||||
"DataLog",
|
||||
"Pneumatics",
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <frc/DriverStation.h>
|
||||
#include <frc/internal/DriverStationModeThread.h>
|
||||
#include <frc/livewindow/LiveWindow.h>
|
||||
#include <frc/shuffleboard/Shuffleboard.h>
|
||||
#include <hal/DriverStation.h>
|
||||
#include <networktables/NetworkTable.h>
|
||||
|
||||
@@ -47,7 +46,6 @@ void Robot::StartCompetition() {
|
||||
}
|
||||
} else if (IsTest()) {
|
||||
frc::LiveWindow::SetEnabled(true);
|
||||
frc::Shuffleboard::EnableActuatorWidgets();
|
||||
modeThread.InTest(true);
|
||||
Test();
|
||||
modeThread.InTest(false);
|
||||
@@ -55,7 +53,6 @@ void Robot::StartCompetition() {
|
||||
wpi::WaitForObject(event.GetHandle());
|
||||
}
|
||||
frc::LiveWindow::SetEnabled(false);
|
||||
frc::Shuffleboard::DisableActuatorWidgets();
|
||||
} else {
|
||||
modeThread.InTeleop(true);
|
||||
Teleop();
|
||||
|
||||
@@ -10,7 +10,6 @@ import edu.wpi.first.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.hal.HAL;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
|
||||
import java.util.ConcurrentModificationException;
|
||||
|
||||
@@ -331,7 +330,6 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
case kTest -> {
|
||||
if (m_lwEnabledInTest) {
|
||||
LiveWindow.setEnabled(false);
|
||||
Shuffleboard.disableActuatorWidgets();
|
||||
}
|
||||
testExit();
|
||||
}
|
||||
@@ -357,7 +355,6 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
case kTest -> {
|
||||
if (m_lwEnabledInTest) {
|
||||
LiveWindow.setEnabled(true);
|
||||
Shuffleboard.enableActuatorWidgets();
|
||||
}
|
||||
testInit();
|
||||
m_watchdog.addEpoch("testInit()");
|
||||
@@ -404,8 +401,6 @@ public abstract class IterativeRobotBase extends RobotBase {
|
||||
m_watchdog.addEpoch("SmartDashboard.updateValues()");
|
||||
LiveWindow.updateValues();
|
||||
m_watchdog.addEpoch("LiveWindow.updateValues()");
|
||||
Shuffleboard.update();
|
||||
m_watchdog.addEpoch("Shuffleboard.update()");
|
||||
|
||||
if (isSimulation()) {
|
||||
HAL.simPeriodicBefore();
|
||||
|
||||
@@ -18,7 +18,6 @@ import edu.wpi.first.networktables.NetworkTableEvent;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
|
||||
import edu.wpi.first.wpilibj.util.WPILibVersion;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -227,7 +226,6 @@ public abstract class RobotBase implements AutoCloseable {
|
||||
});
|
||||
|
||||
LiveWindow.setEnabled(false);
|
||||
Shuffleboard.disableActuatorWidgets();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this 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;
|
||||
}
|
||||
}
|
||||
@@ -1,476 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* The types of the widgets bundled with Shuffleboard.
|
||||
*
|
||||
* <p>For example, setting a number to be displayed with a slider:
|
||||
*
|
||||
* <pre>{@code
|
||||
* GenericEntry example = Shuffleboard.getTab("My Tab")
|
||||
* .add("My Number", 0)
|
||||
* .withWidget(BuiltInWidgets.kNumberSlider)
|
||||
* .withProperties(Map.of("min", 0, "max", 1))
|
||||
* .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.
|
||||
*/
|
||||
public enum BuiltInWidgets implements WidgetType {
|
||||
/**
|
||||
* Displays a value with a simple text field. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>String
|
||||
* <li>Number
|
||||
* <li>Boolean
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kTextView("Text View"),
|
||||
/**
|
||||
* Displays a number with a controllable slider. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Number
|
||||
* </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>
|
||||
*/
|
||||
kNumberSlider("Number Slider"),
|
||||
/**
|
||||
* Displays a number with a view-only bar. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Number
|
||||
* </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>
|
||||
*/
|
||||
kNumberBar("Number Bar"),
|
||||
/**
|
||||
* Displays a number with a view-only dial. Displayed values are rounded to the nearest integer.
|
||||
* <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Number
|
||||
* </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>
|
||||
*/
|
||||
kDial("Simple Dial"),
|
||||
/**
|
||||
* 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>Number array
|
||||
* </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>
|
||||
*/
|
||||
kGraph("Graph"),
|
||||
/**
|
||||
* Displays a boolean value as a large colored box. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Boolean
|
||||
* </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>
|
||||
*/
|
||||
kBooleanBox("Boolean Box"),
|
||||
/**
|
||||
* Displays a boolean with a large interactive toggle button. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Boolean
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kToggleButton("Toggle Button"),
|
||||
/**
|
||||
* Displays a boolean with a fixed-size toggle switch. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Boolean
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kToggleSwitch("Toggle Switch"),
|
||||
/**
|
||||
* Displays an analog input or a raw number with a number bar. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Number
|
||||
* <li>{@link edu.wpi.first.wpilibj.AnalogInput}
|
||||
* </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>
|
||||
*/
|
||||
kVoltageView("Voltage View"),
|
||||
/**
|
||||
* Displays a {@link edu.wpi.first.wpilibj.PowerDistribution PowerDistribution}. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.PowerDistribution}
|
||||
* </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>
|
||||
*/
|
||||
kPowerDistribution("PDP"),
|
||||
/**
|
||||
* 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}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kComboBoxChooser("ComboBox Chooser"),
|
||||
/**
|
||||
* 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}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kSplitButtonChooser("Split Button Chooser"),
|
||||
/**
|
||||
* Displays an {@link edu.wpi.first.wpilibj.Encoder} displaying its speed, total traveled
|
||||
* distance, and its distance per tick. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.Encoder}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kEncoder("Encoder"),
|
||||
/**
|
||||
* Displays a {@link edu.wpi.first.wpilibj.motorcontrol.MotorController MotorController}. The
|
||||
* motor 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.motorcontrol.PWMMotorController}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.DMC60}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.Jaguar}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.PWMTalonFX}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.PWMTalonSRX}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.PWMVenom}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.PWMVictorSPX}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.SD540}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.Spark}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.Talon}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.Victor}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.VictorSP}
|
||||
* <li>{@link edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup}
|
||||
* <li>Any custom subclass of {@code MotorController}
|
||||
* </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>
|
||||
*/
|
||||
kMotorController("Motor Controller"),
|
||||
/**
|
||||
* 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.wpilibj2.command.Command}
|
||||
* <li>Any custom subclass of {@code Command}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kCommand("Command"),
|
||||
/**
|
||||
* 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.wpilibj2.command.PIDCommand}
|
||||
* <li>Any custom subclass of {@code PIDCommand}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kPIDCommand("PID Command"),
|
||||
/**
|
||||
* 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.math.controller.PIDController}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kPIDController("PID Controller"),
|
||||
/**
|
||||
* Displays an accelerometer with a number bar displaying the magnitude of the acceleration and
|
||||
* text displaying the exact value. <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>
|
||||
*/
|
||||
kAccelerometer("Accelerometer"),
|
||||
/**
|
||||
* Displays a 3-axis accelerometer with a number bar for each axis' acceleration. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.ADXL345_I2C}
|
||||
* </ul>
|
||||
*
|
||||
* <br>
|
||||
* Custom properties:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Name</th><th>Type</th><th>Default Value</th><th>Notes</th></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>
|
||||
*/
|
||||
k3AxisAccelerometer("3-Axis Accelerometer"),
|
||||
/**
|
||||
* Displays a gyro with a dial from 0 to 360 degrees. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.AnalogGyro}
|
||||
* <li>Any custom subclass of {@code GyroBase} (such as a MXP gyro)
|
||||
* </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>
|
||||
*/
|
||||
kGyro("Gyro"),
|
||||
/**
|
||||
* Displays a relay with toggle buttons for each supported mode (off, on, forward, reverse). <br>
|
||||
* This widget has no custom properties.
|
||||
*/
|
||||
kRelay("Relay"),
|
||||
/**
|
||||
* 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}
|
||||
* </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>
|
||||
*/
|
||||
kDifferentialDrive("Differential Drivebase"),
|
||||
/**
|
||||
* 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}
|
||||
* </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>
|
||||
*/
|
||||
kMecanumDrive("Mecanum Drivebase"),
|
||||
/**
|
||||
* Displays a camera stream. <br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.cscore.VideoSource} (as long as it is streaming on an MJPEG server)
|
||||
* </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>
|
||||
*/
|
||||
kCameraStream("Camera Stream"),
|
||||
/**
|
||||
* Displays a Field2d object.<br>
|
||||
* Supported types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link edu.wpi.first.wpilibj.smartdashboard.Field2d}
|
||||
* </ul>
|
||||
*/
|
||||
kField("Field"),
|
||||
;
|
||||
|
||||
private final String m_widgetName;
|
||||
|
||||
BuiltInWidgets(String widgetName) {
|
||||
this.m_widgetName = widgetName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWidgetName() {
|
||||
return m_widgetName;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SendableBuilderImpl;
|
||||
|
||||
/**
|
||||
* A Shuffleboard widget that handles a {@link Sendable} object such as a motor controller or
|
||||
* sensor.
|
||||
*/
|
||||
public final class ComplexWidget extends ShuffleboardWidget<ComplexWidget> {
|
||||
private final Sendable m_sendable;
|
||||
private SendableBuilderImpl m_builder;
|
||||
|
||||
ComplexWidget(ShuffleboardContainer parent, String title, Sendable sendable) {
|
||||
super(parent, title);
|
||||
m_sendable = sendable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildInto(NetworkTable parentTable, NetworkTable metaTable) {
|
||||
buildMetadata(metaTable);
|
||||
if (m_builder == null) {
|
||||
m_builder = new SendableBuilderImpl();
|
||||
m_builder.setTable(parentTable.getSubTable(getTitle()));
|
||||
m_sendable.initSendable(m_builder);
|
||||
m_builder.startListeners();
|
||||
}
|
||||
m_builder.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables user control of this widget in the Shuffleboard application. This method is
|
||||
* package-private to prevent users from enabling control themselves. Has no effect if the
|
||||
* sendable is not marked as an actuator with {@link SendableBuilder#setActuator}.
|
||||
*/
|
||||
void enableIfActuator() {
|
||||
if (m_builder.isActuator()) {
|
||||
m_builder.startLiveWindowMode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables user control of this widget in the Shuffleboard application. This method is
|
||||
* package-private to prevent users from enabling control themselves. Has no effect if the
|
||||
* sendable is not marked as an actuator with {@link SendableBuilder#setActuator}.
|
||||
*/
|
||||
void disableIfActuator() {
|
||||
if (m_builder.isActuator()) {
|
||||
m_builder.stopLiveWindowMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.networktables.GenericPublisher;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableType;
|
||||
import edu.wpi.first.util.function.FloatSupplier;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import edu.wpi.first.util.sendable.SendableRegistry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.LongSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** A helper class for Shuffleboard containers to handle common child operations. */
|
||||
final class ContainerHelper {
|
||||
private final ShuffleboardContainer m_container;
|
||||
private final Set<String> m_usedTitles = new HashSet<>();
|
||||
private final List<ShuffleboardComponent<?>> m_components = new ArrayList<>();
|
||||
private final Map<String, ShuffleboardLayout> m_layouts = new LinkedHashMap<>();
|
||||
|
||||
ContainerHelper(ShuffleboardContainer container) {
|
||||
m_container = container;
|
||||
}
|
||||
|
||||
List<ShuffleboardComponent<?>> getComponents() {
|
||||
return m_components;
|
||||
}
|
||||
|
||||
ShuffleboardLayout getLayout(String title, String type) {
|
||||
if (!m_layouts.containsKey(title)) {
|
||||
ShuffleboardLayout layout = new ShuffleboardLayout(m_container, title, type);
|
||||
m_components.add(layout);
|
||||
m_layouts.put(title, layout);
|
||||
}
|
||||
return m_layouts.get(title);
|
||||
}
|
||||
|
||||
ShuffleboardLayout getLayout(String title) {
|
||||
ShuffleboardLayout layout = m_layouts.get(title);
|
||||
if (layout == null) {
|
||||
throw new NoSuchElementException("No layout has been defined with the title '" + title + "'");
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
ComplexWidget add(String title, Sendable sendable) {
|
||||
requireNonNullParam(sendable, "sendable", "add");
|
||||
checkTitle(title);
|
||||
ComplexWidget widget = new ComplexWidget(m_container, title, sendable);
|
||||
m_components.add(widget);
|
||||
return widget;
|
||||
}
|
||||
|
||||
ComplexWidget add(Sendable sendable) {
|
||||
requireNonNullParam(sendable, "sendable", "add");
|
||||
String name = SendableRegistry.getName(sendable);
|
||||
if (name.isEmpty()) {
|
||||
throw new IllegalArgumentException("Sendable must have a name");
|
||||
}
|
||||
return add(name, sendable);
|
||||
}
|
||||
|
||||
SimpleWidget add(String title, Object defaultValue) {
|
||||
requireNonNullParam(defaultValue, "defaultValue", "add");
|
||||
return add(title, NetworkTableType.getStringFromObject(defaultValue), defaultValue);
|
||||
}
|
||||
|
||||
SimpleWidget add(String title, String typeString, Object defaultValue) {
|
||||
requireNonNullParam(title, "title", "add");
|
||||
requireNonNullParam(defaultValue, "defaultValue", "add");
|
||||
checkTitle(title);
|
||||
checkNtType(defaultValue);
|
||||
|
||||
SimpleWidget widget = new SimpleWidget(m_container, title);
|
||||
m_components.add(widget);
|
||||
widget.getEntry(typeString).setDefaultValue(defaultValue);
|
||||
return widget;
|
||||
}
|
||||
|
||||
SuppliedValueWidget<String> addString(String title, Supplier<String> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addString");
|
||||
return addSupplied(title, "string", valueSupplier, GenericPublisher::setString);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Double> addNumber(String title, DoubleSupplier valueSupplier) {
|
||||
requireNonNullParam(title, "title", "addNumber");
|
||||
requireNonNullParam(valueSupplier, "valueSupplier", "addNumber");
|
||||
return addDouble(title, valueSupplier);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Double> addDouble(String title, DoubleSupplier valueSupplier) {
|
||||
precheck(title, valueSupplier, "addDouble");
|
||||
return addSupplied(title, "double", valueSupplier::getAsDouble, GenericPublisher::setDouble);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Float> addFloat(String title, FloatSupplier valueSupplier) {
|
||||
precheck(title, valueSupplier, "addFloat");
|
||||
return addSupplied(title, "float", valueSupplier::getAsFloat, GenericPublisher::setFloat);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Long> addInteger(String title, LongSupplier valueSupplier) {
|
||||
precheck(title, valueSupplier, "addInteger");
|
||||
return addSupplied(title, "int", valueSupplier::getAsLong, GenericPublisher::setInteger);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<Boolean> addBoolean(String title, BooleanSupplier valueSupplier) {
|
||||
precheck(title, valueSupplier, "addBoolean");
|
||||
return addSupplied(title, "boolean", valueSupplier::getAsBoolean, GenericPublisher::setBoolean);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<String[]> addStringArray(String title, Supplier<String[]> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addStringArray");
|
||||
return addSupplied(title, "string[]", valueSupplier, GenericPublisher::setStringArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<double[]> addDoubleArray(String title, Supplier<double[]> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addDoubleArray");
|
||||
return addSupplied(title, "double[]", valueSupplier, GenericPublisher::setDoubleArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<float[]> addFloatArray(String title, Supplier<float[]> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addFloatArray");
|
||||
return addSupplied(title, "float[]", valueSupplier, GenericPublisher::setFloatArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<long[]> addIntegerArray(String title, Supplier<long[]> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addIntegerArray");
|
||||
return addSupplied(title, "int[]", valueSupplier, GenericPublisher::setIntegerArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<boolean[]> addBooleanArray(String title, Supplier<boolean[]> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addBooleanArray");
|
||||
return addSupplied(title, "boolean[]", valueSupplier, GenericPublisher::setBooleanArray);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<byte[]> addRaw(String title, Supplier<byte[]> valueSupplier) {
|
||||
return addRaw(title, "raw", valueSupplier);
|
||||
}
|
||||
|
||||
SuppliedValueWidget<byte[]> addRaw(
|
||||
String title, String typeString, Supplier<byte[]> valueSupplier) {
|
||||
precheck(title, valueSupplier, "addRaw");
|
||||
return addSupplied(title, typeString, valueSupplier, GenericPublisher::setRaw);
|
||||
}
|
||||
|
||||
private void precheck(String title, Object valueSupplier, String methodName) {
|
||||
requireNonNullParam(title, "title", methodName);
|
||||
requireNonNullParam(valueSupplier, "valueSupplier", methodName);
|
||||
checkTitle(title);
|
||||
}
|
||||
|
||||
private <T> SuppliedValueWidget<T> addSupplied(
|
||||
String title,
|
||||
String typeString,
|
||||
Supplier<T> supplier,
|
||||
BiConsumer<GenericPublisher, T> setter) {
|
||||
SuppliedValueWidget<T> widget =
|
||||
new SuppliedValueWidget<>(m_container, title, typeString, supplier, setter);
|
||||
m_components.add(widget);
|
||||
return widget;
|
||||
}
|
||||
|
||||
private static void checkNtType(Object data) {
|
||||
if (!NetworkTableEntry.isValidDataType(data)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot add data of type " + data.getClass().getName() + " to Shuffleboard");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkTitle(String title) {
|
||||
if (m_usedTitles.contains(title)) {
|
||||
throw new IllegalArgumentException("Title is already in use: " + title);
|
||||
}
|
||||
m_usedTitles.add(title);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* The importance of an event marker in Shuffleboard. The exact meaning of each importance level is
|
||||
* up for interpretation on a team-to-team basis, but users should follow the general guidelines of
|
||||
* the various importance levels. The examples given are for reference and may be ignored or
|
||||
* considered to be more or less important from team to team.
|
||||
*/
|
||||
public enum EventImportance {
|
||||
// Maintainer note: this enum is mirrored in WPILibC and in Shuffleboard
|
||||
// Modifying the enum or enum strings requires a corresponding change to the C++ enum
|
||||
// and the enum in Shuffleboard
|
||||
|
||||
/** A trivial event such as a change in command state. */
|
||||
kTrivial("TRIVIAL"),
|
||||
|
||||
/** A low importance event such as acquisition of a game piece. */
|
||||
kLow("LOW"),
|
||||
|
||||
/**
|
||||
* A "normal" importance event, such as a transition from autonomous mode to teleoperated control.
|
||||
*/
|
||||
kNormal("NORMAL"),
|
||||
|
||||
/** A high-importance event such as scoring a game piece. */
|
||||
kHigh("HIGH"),
|
||||
|
||||
/** A critically important event such as a brownout, component failure, or software deadlock. */
|
||||
kCritical("CRITICAL");
|
||||
|
||||
private final String m_simpleName;
|
||||
|
||||
EventImportance(String simpleName) {
|
||||
m_simpleName = simpleName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of the given enum.
|
||||
*
|
||||
* @return Name of the given enum.
|
||||
*/
|
||||
public String getSimpleName() {
|
||||
return m_simpleName;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* 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 BuiltInWidgets the built-in widget types
|
||||
*/
|
||||
public interface LayoutType {
|
||||
/**
|
||||
* Gets the string type of the layout as defined by that layout in Shuffleboard.
|
||||
*
|
||||
* @return The string type of the layout.
|
||||
*/
|
||||
String getLayoutName();
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.BooleanPublisher;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.wpilibj.DriverStation;
|
||||
|
||||
/** Controls Shuffleboard recordings via NetworkTables. */
|
||||
final class RecordingController {
|
||||
private static final String kRecordingTableName = "/Shuffleboard/.recording/";
|
||||
private static final String kRecordingControlKey = kRecordingTableName + "RecordData";
|
||||
private static final String kRecordingFileNameFormatKey = kRecordingTableName + "FileNameFormat";
|
||||
private static final String kEventMarkerTableName = kRecordingTableName + "events";
|
||||
|
||||
private final BooleanPublisher m_recordingControlEntry;
|
||||
private final StringPublisher m_recordingFileNameFormatEntry;
|
||||
private final NetworkTable m_eventsTable;
|
||||
|
||||
RecordingController(NetworkTableInstance ntInstance) {
|
||||
m_recordingControlEntry = ntInstance.getBooleanTopic(kRecordingControlKey).publish();
|
||||
m_recordingFileNameFormatEntry =
|
||||
ntInstance.getStringTopic(kRecordingFileNameFormatKey).publish();
|
||||
m_eventsTable = ntInstance.getTable(kEventMarkerTableName);
|
||||
}
|
||||
|
||||
public void startRecording() {
|
||||
m_recordingControlEntry.set(true);
|
||||
}
|
||||
|
||||
public void stopRecording() {
|
||||
m_recordingControlEntry.set(false);
|
||||
}
|
||||
|
||||
public void setRecordingFileNameFormat(String format) {
|
||||
m_recordingFileNameFormatEntry.set(format);
|
||||
}
|
||||
|
||||
public void clearRecordingFileNameFormat() {
|
||||
m_recordingFileNameFormatEntry.set("");
|
||||
}
|
||||
|
||||
public void addEventMarker(String name, String description, EventImportance importance) {
|
||||
if (name == null || name.isEmpty()) {
|
||||
DriverStation.reportError("Shuffleboard event name was not specified", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (importance == null) {
|
||||
DriverStation.reportError("Shuffleboard event importance was null", true);
|
||||
return;
|
||||
}
|
||||
|
||||
String eventDescription = description == null ? "" : description;
|
||||
|
||||
m_eventsTable
|
||||
.getSubTable(name)
|
||||
.getEntry("Info")
|
||||
.setStringArray(new String[] {eventDescription, importance.getSimpleName()});
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.cscore.VideoSource;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.StringArrayPublisher;
|
||||
import edu.wpi.first.networktables.StringArrayTopic;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import edu.wpi.first.util.sendable.SendableBuilder;
|
||||
import edu.wpi.first.util.sendable.SendableRegistry;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/** A wrapper to make video sources sendable and usable from Shuffleboard. */
|
||||
public final class SendableCameraWrapper implements Sendable, AutoCloseable {
|
||||
private static final String kProtocol = "camera_server://";
|
||||
|
||||
private static Map<String, SendableCameraWrapper> m_wrappers = new WeakHashMap<>();
|
||||
|
||||
private static NetworkTable m_table;
|
||||
|
||||
static {
|
||||
setNetworkTableInstance(NetworkTableInstance.getDefault());
|
||||
}
|
||||
|
||||
private final String m_uri;
|
||||
private StringArrayPublisher m_streams;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private SendableCameraWrapper(VideoSource source) {
|
||||
this(source.getName());
|
||||
}
|
||||
|
||||
private SendableCameraWrapper(String cameraName) {
|
||||
SendableRegistry.add(this, cameraName);
|
||||
m_uri = kProtocol + cameraName;
|
||||
}
|
||||
|
||||
private SendableCameraWrapper(String cameraName, String[] cameraUrls) {
|
||||
this(cameraName);
|
||||
|
||||
StringArrayTopic streams = new StringArrayTopic(m_table.getTopic(cameraName + "/streams"));
|
||||
if (streams.exists()) {
|
||||
throw new IllegalStateException(
|
||||
"A camera is already being streamed with the name '" + cameraName + "'");
|
||||
}
|
||||
|
||||
m_streams = streams.publish();
|
||||
m_streams.set(cameraUrls);
|
||||
}
|
||||
|
||||
/** Clears all cached wrapper objects. This should only be used in tests. */
|
||||
static void clearWrappers() {
|
||||
m_wrappers.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
SendableRegistry.remove(this);
|
||||
if (m_streams != null) {
|
||||
m_streams.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets NetworkTable instance used for camera publisher entries.
|
||||
*
|
||||
* @param inst NetworkTable instance
|
||||
*/
|
||||
public static synchronized void setNetworkTableInstance(NetworkTableInstance inst) {
|
||||
m_table = inst.getTable("CameraPublisher");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {@link
|
||||
* ShuffleboardTab#add(Sendable)} and {@link ShuffleboardLayout#add(Sendable)}
|
||||
*/
|
||||
public static SendableCameraWrapper wrap(VideoSource source) {
|
||||
return m_wrappers.computeIfAbsent(source.getName(), name -> new SendableCameraWrapper(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wrapper for an arbitrary camera stream. The stream URLs <i>must</i> be specified
|
||||
* using a host resolvable by a program running on a different host (such as a dashboard); prefer
|
||||
* using static IP addresses (if known) or DHCP identifiers such as {@code "raspberrypi.local"}.
|
||||
*
|
||||
* <p>If a wrapper already exists for the given camera, that wrapper is returned and the specified
|
||||
* URLs are ignored.
|
||||
*
|
||||
* @param cameraName the name of the camera. Cannot be null or empty
|
||||
* @param cameraUrls the URLs with which the camera stream may be accessed. At least one URL must
|
||||
* be specified
|
||||
* @return a sendable wrapper object for the video source, usable in Shuffleboard via {@link
|
||||
* ShuffleboardTab#add(Sendable)} and {@link ShuffleboardLayout#add(Sendable)}
|
||||
*/
|
||||
public static SendableCameraWrapper wrap(String cameraName, String... cameraUrls) {
|
||||
if (m_wrappers.containsKey(cameraName)) {
|
||||
return m_wrappers.get(cameraName);
|
||||
}
|
||||
|
||||
requireNonNullParam(cameraName, "cameraName", "wrap");
|
||||
requireNonNullParam(cameraUrls, "cameraUrls", "wrap");
|
||||
if (cameraName.isEmpty()) {
|
||||
throw new IllegalArgumentException("Camera name not specified");
|
||||
}
|
||||
if (cameraUrls.length == 0) {
|
||||
throw new IllegalArgumentException("No camera URLs specified");
|
||||
}
|
||||
for (int i = 0; i < cameraUrls.length; i++) {
|
||||
Objects.requireNonNull(cameraUrls[i], "Camera URL at index " + i + " was null");
|
||||
}
|
||||
|
||||
SendableCameraWrapper wrapper = new SendableCameraWrapper(cameraName, cameraUrls);
|
||||
m_wrappers.put(cameraName, wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSendable(SendableBuilder builder) {
|
||||
builder.addStringProperty(".ShuffleboardURI", () -> m_uri, null);
|
||||
}
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
|
||||
/**
|
||||
* The Shuffleboard class provides a mechanism with which data can be added and laid out in the
|
||||
* Shuffleboard dashboard application from a robot program. Tabs and layouts can be specified, as
|
||||
* well as choosing which widgets to display with and setting properties of these widgets; for
|
||||
* example, programmers can specify a specific {@code boolean} value to be displayed with a toggle
|
||||
* button instead of the default colored box, or set custom colors for that box.
|
||||
*
|
||||
* <p>For example, displaying a boolean entry with a toggle button:
|
||||
*
|
||||
* <pre>{@code
|
||||
* GenericEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* .add("My Boolean", false)
|
||||
* .withWidget("Toggle Button")
|
||||
* .getEntry();
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Changing the colors of the boolean box:
|
||||
*
|
||||
* <pre>{@code
|
||||
* GenericEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* .add("My Boolean", false)
|
||||
* .withWidget("Boolean Box")
|
||||
* .withProperties(Map.of("colorWhenTrue", "green", "colorWhenFalse", "maroon"))
|
||||
* .getEntry();
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Specifying a parent layout. Note that the layout type must <i>always</i> be specified, even if
|
||||
* the layout has already been generated by a previously defined entry.
|
||||
*
|
||||
* <pre>{@code
|
||||
* GenericEntry myBoolean = Shuffleboard.getTab("Example Tab")
|
||||
* .getLayout("List", "Example List")
|
||||
* .add("My Boolean", false)
|
||||
* .withWidget("Toggle Button")
|
||||
* .getEntry();
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Teams are encouraged to set up shuffleboard layouts at the start of the robot program.
|
||||
*/
|
||||
public final class Shuffleboard {
|
||||
/** The name of the base NetworkTable into which all Shuffleboard data will be added. */
|
||||
public static final String kBaseTableName = "/Shuffleboard";
|
||||
|
||||
private static final ShuffleboardRoot root =
|
||||
new ShuffleboardInstance(NetworkTableInstance.getDefault());
|
||||
private static final RecordingController recordingController =
|
||||
new RecordingController(NetworkTableInstance.getDefault());
|
||||
|
||||
private Shuffleboard() {
|
||||
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all the values in Shuffleboard. Iterative and timed robots are pre-configured to call
|
||||
* this method in the main robot loop; teams using custom robot base classes, or subclass
|
||||
* SampleRobot, should make sure to call this repeatedly to keep data on the dashboard up to date.
|
||||
*/
|
||||
public static void update() {
|
||||
root.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Shuffleboard tab with the given title, creating it if it does not already exist.
|
||||
*
|
||||
* @param title the title of the tab
|
||||
* @return the tab with the given title
|
||||
*/
|
||||
public static ShuffleboardTab getTab(String title) {
|
||||
return root.getTab(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the tab in the dashboard with the given index in the range [0..n-1], where <i>n</i> is
|
||||
* the number of tabs in the dashboard at the time this method is called.
|
||||
*
|
||||
* @param index the index of the tab to select
|
||||
*/
|
||||
public static void selectTab(int index) {
|
||||
root.selectTab(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the tab in the dashboard with the given title.
|
||||
*
|
||||
* @param title the title of the tab to select
|
||||
*/
|
||||
public static void selectTab(String title) {
|
||||
root.selectTab(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables user control of widgets containing actuators: motor controllers, relays, etc. This
|
||||
* should only be used when the robot is in test mode. IterativeRobotBase and SampleRobot are both
|
||||
* configured to call this method when entering test mode; most users should not need to use this
|
||||
* method directly.
|
||||
*/
|
||||
public static void enableActuatorWidgets() {
|
||||
root.enableActuatorWidgets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables user control of widgets containing actuators. For safety reasons, actuators should
|
||||
* only be controlled while in test mode. IterativeRobotBase and SampleRobot are both configured
|
||||
* to call this method when exiting in test mode; most users should not need to use this method
|
||||
* directly.
|
||||
*/
|
||||
public static void disableActuatorWidgets() {
|
||||
update(); // Need to update to make sure the sendable builders are initialized
|
||||
root.disableActuatorWidgets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts data recording on the dashboard. Has no effect if recording is already in progress.
|
||||
*
|
||||
* @see #stopRecording()
|
||||
*/
|
||||
public static void startRecording() {
|
||||
recordingController.startRecording();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops data recording on the dashboard. Has no effect if no recording is in progress.
|
||||
*
|
||||
* @see #startRecording()
|
||||
*/
|
||||
public static void stopRecording() {
|
||||
recordingController.stopRecording();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file name format for new recording files to use. If recording is in progress when this
|
||||
* method is called, it will continue to use the same file. New recordings will use the format.
|
||||
*
|
||||
* <p>To avoid recording files overwriting each other, make sure to use unique recording file
|
||||
* names. File name formats accept templates for inserting the date and time when the recording
|
||||
* started with the {@code ${date}} and {@code ${time}} templates, respectively. For example, the
|
||||
* default format is {@code "recording-${time}"} and recording files created with it will have
|
||||
* names like {@code "recording-2018.01.15.sbr"}. Users are <strong>strongly</strong> recommended
|
||||
* to use the {@code ${time}} template to ensure unique file names.
|
||||
*
|
||||
* @param format the format for the
|
||||
* @see #clearRecordingFileNameFormat()
|
||||
*/
|
||||
public static void setRecordingFileNameFormat(String format) {
|
||||
recordingController.setRecordingFileNameFormat(format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the custom name format for recording files. New recordings will use the default format.
|
||||
*
|
||||
* @see #setRecordingFileNameFormat(String)
|
||||
*/
|
||||
public static void clearRecordingFileNameFormat() {
|
||||
recordingController.clearRecordingFileNameFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies Shuffleboard of an event. Events can range from as trivial as a change in a command
|
||||
* state to as critical as a total power loss or component failure. If Shuffleboard is recording,
|
||||
* the event will also be recorded.
|
||||
*
|
||||
* <p>If {@code name} is {@code null} or empty, or {@code importance} is {@code null}, then no
|
||||
* event will be sent and an error will be printed to the driver station.
|
||||
*
|
||||
* @param name the name of the event
|
||||
* @param description a description of the event
|
||||
* @param importance the importance of the event
|
||||
*/
|
||||
public static void addEventMarker(String name, String description, EventImportance importance) {
|
||||
recordingController.addEventMarker(name, description, importance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies Shuffleboard of an event. Events can range from as trivial as a change in a command
|
||||
* state to as critical as a total power loss or component failure. If Shuffleboard is recording,
|
||||
* the event will also be recorded.
|
||||
*
|
||||
* <p>If {@code name} is {@code null} or empty, or {@code importance} is {@code null}, then no
|
||||
* event will be sent and an error will be printed to the driver station.
|
||||
*
|
||||
* @param name the name of the event
|
||||
* @param importance the importance of the event
|
||||
*/
|
||||
public static void addEventMarker(String name, EventImportance importance) {
|
||||
addEventMarker(name, "", importance);
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A generic component in Shuffleboard.
|
||||
*
|
||||
* @param <C> the self type
|
||||
*/
|
||||
public abstract class ShuffleboardComponent<C extends ShuffleboardComponent<C>>
|
||||
implements ShuffleboardValue {
|
||||
private final ShuffleboardContainer m_parent;
|
||||
private final String m_title;
|
||||
private String m_type;
|
||||
private Map<String, Object> m_properties;
|
||||
private boolean m_metadataDirty = true;
|
||||
private int m_column = -1;
|
||||
private int m_row = -1;
|
||||
private int m_width = -1;
|
||||
private int m_height = -1;
|
||||
|
||||
/**
|
||||
* Constructs a ShuffleboardComponent.
|
||||
*
|
||||
* @param parent The parent container.
|
||||
* @param title The component title.
|
||||
* @param type The component type.
|
||||
*/
|
||||
protected ShuffleboardComponent(ShuffleboardContainer parent, String title, String type) {
|
||||
m_parent = requireNonNullParam(parent, "parent", "ShuffleboardComponent");
|
||||
m_title = requireNonNullParam(title, "title", "ShuffleboardComponent");
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a ShuffleboardComponent.
|
||||
*
|
||||
* @param parent The parent container.
|
||||
* @param title The component title.
|
||||
*/
|
||||
protected ShuffleboardComponent(ShuffleboardContainer parent, String title) {
|
||||
this(parent, title, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent container.
|
||||
*
|
||||
* @return The parent container.
|
||||
*/
|
||||
public final ShuffleboardContainer getParent() {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the component type.
|
||||
*
|
||||
* @param type The component type.
|
||||
*/
|
||||
protected final void setType(String type) {
|
||||
m_type = type;
|
||||
m_metadataDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component type.
|
||||
*
|
||||
* @return The component type.
|
||||
*/
|
||||
public final String getType() {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getTitle() {
|
||||
return m_title;
|
||||
}
|
||||
|
||||
/** Gets the custom properties for this component. May be null. */
|
||||
final Map<String, Object> getProperties() {
|
||||
return m_properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets custom properties for this component. Property names are case- and whitespace-insensitive
|
||||
* (capitalization and spaces do not matter).
|
||||
*
|
||||
* @param properties the properties for this component
|
||||
* @return this component
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final C withProperties(Map<String, Object> properties) {
|
||||
m_properties = properties;
|
||||
m_metadataDirty = true;
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position of this component in the tab. This has no effect if this component is inside
|
||||
* a layout.
|
||||
*
|
||||
* <p>If the position of a single component is set, it is recommended to set the positions of
|
||||
* <i>all</i> components inside a tab to prevent Shuffleboard from automatically placing another
|
||||
* component there before the one with the specific position is sent.
|
||||
*
|
||||
* @param columnIndex the column in the tab to place this component
|
||||
* @param rowIndex the row in the tab to place this component
|
||||
* @return this component
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final C withPosition(int columnIndex, int rowIndex) {
|
||||
m_column = columnIndex;
|
||||
m_row = rowIndex;
|
||||
m_metadataDirty = true;
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of this component in the tab. This has no effect if this component is inside a
|
||||
* layout.
|
||||
*
|
||||
* @param width how many columns wide the component should be
|
||||
* @param height how many rows high the component should be
|
||||
* @return this component
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final C withSize(int width, int height) {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_metadataDirty = true;
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds NT metadata.
|
||||
*
|
||||
* @param metaTable The NT metadata table.
|
||||
*/
|
||||
protected final void buildMetadata(NetworkTable metaTable) {
|
||||
if (!m_metadataDirty) {
|
||||
return;
|
||||
}
|
||||
// Component type
|
||||
if (getType() == null) {
|
||||
metaTable.getEntry("PreferredComponent").unpublish();
|
||||
} else {
|
||||
metaTable.getEntry("PreferredComponent").setString(getType());
|
||||
}
|
||||
|
||||
// Tile size
|
||||
if (m_width <= 0 || m_height <= 0) {
|
||||
metaTable.getEntry("Size").unpublish();
|
||||
} else {
|
||||
metaTable.getEntry("Size").setDoubleArray(new double[] {m_width, m_height});
|
||||
}
|
||||
|
||||
// Tile position
|
||||
if (m_column < 0 || m_row < 0) {
|
||||
metaTable.getEntry("Position").unpublish();
|
||||
} else {
|
||||
metaTable.getEntry("Position").setDoubleArray(new double[] {m_column, m_row});
|
||||
}
|
||||
|
||||
// Custom properties
|
||||
if (getProperties() != null) {
|
||||
NetworkTable propTable = metaTable.getSubTable("Properties");
|
||||
getProperties().forEach((name, value) -> propTable.getEntry(name).setValue(value));
|
||||
}
|
||||
m_metadataDirty = false;
|
||||
}
|
||||
}
|
||||
@@ -1,363 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.cscore.VideoSource;
|
||||
import edu.wpi.first.networktables.NetworkTableType;
|
||||
import edu.wpi.first.util.function.FloatSupplier;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.LongSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** Common interface for objects that can contain shuffleboard components. */
|
||||
public sealed interface ShuffleboardContainer extends ShuffleboardValue
|
||||
permits ShuffleboardLayout, ShuffleboardTab {
|
||||
/**
|
||||
* Gets the components that are direct children of this container.
|
||||
*
|
||||
* @return The components that are direct children of this container.
|
||||
*/
|
||||
List<ShuffleboardComponent<?>> getComponents();
|
||||
|
||||
/**
|
||||
* 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(String title, String 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.
|
||||
*
|
||||
* @param title the title of the layout
|
||||
* @param layoutType the type of the layout, eg "List" or "Grid"
|
||||
* @return the layout
|
||||
*/
|
||||
default ShuffleboardLayout getLayout(String title, LayoutType layoutType) {
|
||||
return getLayout(title, layoutType.getLayoutName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 NoSuchElementException if no layout has yet been defined with the given title
|
||||
*/
|
||||
ShuffleboardLayout getLayout(String title);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given sendable.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param sendable the sendable 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(String 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
|
||||
*/
|
||||
default ComplexWidget add(String title, VideoSource video) {
|
||||
return add(title, SendableCameraWrapper.wrap(video));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given sendable.
|
||||
*
|
||||
* @param sendable the sendable to display
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title, or if the sendable's name has not been specified
|
||||
*/
|
||||
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
|
||||
*/
|
||||
default ComplexWidget add(VideoSource video) {
|
||||
return add(SendableCameraWrapper.wrap(video));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
* @see #addPersistent(String, Object) add(String title, Object defaultValue)
|
||||
*/
|
||||
SimpleWidget add(String title, Object defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display the given data.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param typeString the NT type string
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
* @see #addPersistent(String, Object) add(String title, Object defaultValue)
|
||||
*/
|
||||
SimpleWidget add(String title, String typeString, Object defaultValue);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a video stream.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param cameraName the name of the streamed camera
|
||||
* @param cameraUrls the URLs with which the dashboard can access the camera stream
|
||||
* @return a widget to display the camera stream
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
default ComplexWidget addCamera(String title, String cameraName, String... cameraUrls) {
|
||||
return add(title, SendableCameraWrapper.wrap(cameraName, cameraUrls));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<String> addString(String title, Supplier<String> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<Double> addNumber(String title, DoubleSupplier valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<Double> addDouble(String title, DoubleSupplier valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<Float> addFloat(String title, FloatSupplier valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<Long> addInteger(String title, LongSupplier valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<Boolean> addBoolean(String title, BooleanSupplier valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<String[]> addStringArray(String title, Supplier<String[]> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<double[]> addDoubleArray(String title, Supplier<double[]> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<float[]> addFloatArray(String title, Supplier<float[]> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<long[]> addIntegerArray(String title, Supplier<long[]> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<boolean[]> addBooleanArray(String title, Supplier<boolean[]> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
default SuppliedValueWidget<byte[]> addRaw(String title, Supplier<byte[]> valueSupplier) {
|
||||
return addRaw(title, "raw", valueSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a widget to this container. The widget will display the data provided by the value
|
||||
* supplier. Changes made on the dashboard will not propagate to the widget object, and will be
|
||||
* overridden by values from the value supplier.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param typeString the NT type string for the value
|
||||
* @param valueSupplier the supplier for values
|
||||
* @return a widget to display data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
*/
|
||||
SuppliedValueWidget<byte[]> addRaw(
|
||||
String title, String typeString, Supplier<byte[]> valueSupplier);
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data. Unlike {@link #add(String,
|
||||
* Object)}, the value in the widget will be saved on the robot and will be used when the robot
|
||||
* program next starts rather than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
* @see #add(String, Object) add(String title, Object defaultValue)
|
||||
*/
|
||||
default SimpleWidget addPersistent(String title, Object defaultValue) {
|
||||
return addPersistent(title, NetworkTableType.getStringFromObject(defaultValue), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a widget to this container to display a simple piece of data. Unlike {@link #add(String,
|
||||
* Object)}, the value in the widget will be saved on the robot and will be used when the robot
|
||||
* program next starts rather than {@code defaultValue}.
|
||||
*
|
||||
* @param title the title of the widget
|
||||
* @param typeString the NT type string
|
||||
* @param defaultValue the default value of the widget
|
||||
* @return a widget to display the sendable data
|
||||
* @throws IllegalArgumentException if a widget already exists in this container with the given
|
||||
* title
|
||||
* @see #add(String, Object) add(String title, Object defaultValue)
|
||||
*/
|
||||
default SimpleWidget addPersistent(String title, String typeString, Object defaultValue) {
|
||||
SimpleWidget widget = add(title, defaultValue);
|
||||
widget.getEntry(typeString).getTopic().setPersistent(true);
|
||||
return widget;
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.hal.HAL;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.PubSubOption;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
final class ShuffleboardInstance implements ShuffleboardRoot {
|
||||
private final Map<String, ShuffleboardTab> m_tabs = new LinkedHashMap<>();
|
||||
private boolean m_reported = false; // NOPMD redundant field initializer
|
||||
private boolean m_tabsChanged = false; // NOPMD redundant field initializer
|
||||
private final NetworkTable m_rootTable;
|
||||
private final NetworkTable m_rootMetaTable;
|
||||
private final StringPublisher m_selectedTabPub;
|
||||
|
||||
/**
|
||||
* Creates a new Shuffleboard instance.
|
||||
*
|
||||
* @param ntInstance the NetworkTables instance to use
|
||||
*/
|
||||
ShuffleboardInstance(NetworkTableInstance ntInstance) {
|
||||
requireNonNullParam(ntInstance, "ntInstance", "ShuffleboardInstance");
|
||||
m_rootTable = ntInstance.getTable(Shuffleboard.kBaseTableName);
|
||||
m_rootMetaTable = m_rootTable.getSubTable(".metadata");
|
||||
m_selectedTabPub =
|
||||
m_rootMetaTable.getStringTopic("Selected").publish(PubSubOption.keepDuplicates(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShuffleboardTab getTab(String title) {
|
||||
requireNonNullParam(title, "title", "getTab");
|
||||
if (!m_reported) {
|
||||
HAL.report(tResourceType.kResourceType_Shuffleboard, 0);
|
||||
m_reported = true;
|
||||
}
|
||||
if (!m_tabs.containsKey(title)) {
|
||||
m_tabs.put(title, new ShuffleboardTab(this, title));
|
||||
m_tabsChanged = true;
|
||||
}
|
||||
return m_tabs.get(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
if (m_tabsChanged) {
|
||||
String[] tabTitles =
|
||||
m_tabs.values().stream().map(ShuffleboardTab::getTitle).toArray(String[]::new);
|
||||
m_rootMetaTable.getEntry("Tabs").setStringArray(tabTitles);
|
||||
m_tabsChanged = false;
|
||||
}
|
||||
for (ShuffleboardTab tab : m_tabs.values()) {
|
||||
String title = tab.getTitle();
|
||||
tab.buildInto(m_rootTable, m_rootMetaTable.getSubTable(title));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableActuatorWidgets() {
|
||||
applyToAllComplexWidgets(ComplexWidget::enableIfActuator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableActuatorWidgets() {
|
||||
applyToAllComplexWidgets(ComplexWidget::disableIfActuator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectTab(int index) {
|
||||
selectTab(Integer.toString(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectTab(String title) {
|
||||
m_selectedTabPub.set(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the function {@code func} to all complex widgets in this root, regardless of how they
|
||||
* are nested.
|
||||
*
|
||||
* @param func the function to apply to all complex widgets
|
||||
*/
|
||||
private void applyToAllComplexWidgets(Consumer<ComplexWidget> func) {
|
||||
for (ShuffleboardTab tab : m_tabs.values()) {
|
||||
apply(tab, func);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the function {@code func} to all complex widgets in {@code container}. Helper method
|
||||
* for {@link #applyToAllComplexWidgets}.
|
||||
*/
|
||||
private void apply(ShuffleboardContainer container, Consumer<ComplexWidget> func) {
|
||||
for (ShuffleboardComponent<?> component : container.getComponents()) {
|
||||
if (component instanceof ComplexWidget widget) {
|
||||
func.accept(widget);
|
||||
}
|
||||
if (component instanceof ShuffleboardContainer nestedContainer) {
|
||||
apply(nestedContainer, func);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.util.function.FloatSupplier;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import java.util.List;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.LongSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** A layout in a Shuffleboard tab. Layouts can contain widgets and other layouts. */
|
||||
public final class ShuffleboardLayout extends ShuffleboardComponent<ShuffleboardLayout>
|
||||
implements ShuffleboardContainer {
|
||||
private static final String kSmartDashboardType = "ShuffleboardLayout";
|
||||
|
||||
private final ContainerHelper m_helper = new ContainerHelper(this);
|
||||
|
||||
ShuffleboardLayout(ShuffleboardContainer parent, String title, String type) {
|
||||
super(parent, title, requireNonNullParam(type, "type", "ShuffleboardLayout"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ShuffleboardComponent<?>> getComponents() {
|
||||
return m_helper.getComponents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShuffleboardLayout getLayout(String title, String type) {
|
||||
return m_helper.getLayout(title, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShuffleboardLayout getLayout(String title) {
|
||||
return m_helper.getLayout(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexWidget add(String title, Sendable sendable) {
|
||||
return m_helper.add(title, sendable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexWidget add(Sendable sendable) {
|
||||
return m_helper.add(sendable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleWidget add(String title, Object defaultValue) {
|
||||
return m_helper.add(title, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleWidget add(String title, String typeString, Object defaultValue) {
|
||||
return m_helper.add(title, typeString, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<String> addString(String title, Supplier<String> valueSupplier) {
|
||||
return m_helper.addString(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Double> addNumber(String title, DoubleSupplier valueSupplier) {
|
||||
return m_helper.addNumber(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Double> addDouble(String title, DoubleSupplier valueSupplier) {
|
||||
return m_helper.addDouble(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Float> addFloat(String title, FloatSupplier valueSupplier) {
|
||||
return m_helper.addFloat(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Long> addInteger(String title, LongSupplier valueSupplier) {
|
||||
return m_helper.addInteger(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Boolean> addBoolean(String title, BooleanSupplier valueSupplier) {
|
||||
return m_helper.addBoolean(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<String[]> addStringArray(
|
||||
String title, Supplier<String[]> valueSupplier) {
|
||||
return m_helper.addStringArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<double[]> addDoubleArray(
|
||||
String title, Supplier<double[]> valueSupplier) {
|
||||
return m_helper.addDoubleArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<float[]> addFloatArray(String title, Supplier<float[]> valueSupplier) {
|
||||
return m_helper.addFloatArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<long[]> addIntegerArray(String title, Supplier<long[]> valueSupplier) {
|
||||
return m_helper.addIntegerArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<boolean[]> addBooleanArray(
|
||||
String title, Supplier<boolean[]> valueSupplier) {
|
||||
return m_helper.addBooleanArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<byte[]> addRaw(
|
||||
String title, String typeString, Supplier<byte[]> valueSupplier) {
|
||||
return m_helper.addRaw(title, typeString, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildInto(NetworkTable parentTable, NetworkTable metaTable) {
|
||||
buildMetadata(metaTable);
|
||||
NetworkTable table = parentTable.getSubTable(getTitle());
|
||||
table.getEntry(".type").setString(kSmartDashboardType);
|
||||
table
|
||||
.getEntry(".type")
|
||||
.getTopic()
|
||||
.setProperty("SmartDashboard", "\"" + kSmartDashboardType + "\"");
|
||||
for (ShuffleboardComponent<?> component : getComponents()) {
|
||||
component.buildInto(table, metaTable.getSubTable(component.getTitle()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* The root of the data placed in Shuffleboard. It contains the tabs, but no data is placed directly
|
||||
* in the root.
|
||||
*
|
||||
* <p>This class is package-private to minimize API surface area.
|
||||
*/
|
||||
interface ShuffleboardRoot {
|
||||
/**
|
||||
* Gets the tab with the given title, creating it if it does not already exist.
|
||||
*
|
||||
* @param title the title of the tab
|
||||
* @return the tab with the given title
|
||||
*/
|
||||
ShuffleboardTab getTab(String title);
|
||||
|
||||
/** Updates all tabs. */
|
||||
void update();
|
||||
|
||||
/** Enables all widgets in Shuffleboard that offer user control over actuators. */
|
||||
void enableActuatorWidgets();
|
||||
|
||||
/** Disables all widgets in Shuffleboard that offer user control over actuators. */
|
||||
void disableActuatorWidgets();
|
||||
|
||||
/**
|
||||
* Selects the tab in the dashboard with the given index in the range [0..n-1], where <i>n</i> is
|
||||
* the number of tabs in the dashboard at the time this method is called.
|
||||
*
|
||||
* @param index the index of the tab to select
|
||||
*/
|
||||
void selectTab(int index);
|
||||
|
||||
/**
|
||||
* Selects the tab in the dashboard with the given title.
|
||||
*
|
||||
* @param title the title of the tab to select
|
||||
*/
|
||||
void selectTab(String title);
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.util.function.FloatSupplier;
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import java.util.List;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.LongSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Represents a tab in the Shuffleboard dashboard. Widgets can be added to the tab with {@link
|
||||
* #add(Sendable)}, {@link #add(String, Object)}, and {@link #add(String, Sendable)}. Widgets can
|
||||
* also be added to layouts with {@link #getLayout(String, String)}; layouts can be nested
|
||||
* arbitrarily deep (note that too many levels may make deeper components unusable).
|
||||
*/
|
||||
public final class ShuffleboardTab implements ShuffleboardContainer {
|
||||
private static final String kSmartDashboardType = "ShuffleboardTab";
|
||||
|
||||
private final ContainerHelper m_helper = new ContainerHelper(this);
|
||||
private final ShuffleboardRoot m_root;
|
||||
private final String m_title;
|
||||
|
||||
ShuffleboardTab(ShuffleboardRoot root, String title) {
|
||||
m_root = root;
|
||||
m_title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return m_title;
|
||||
}
|
||||
|
||||
ShuffleboardRoot getRoot() {
|
||||
return m_root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ShuffleboardComponent<?>> getComponents() {
|
||||
return m_helper.getComponents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShuffleboardLayout getLayout(String title, String type) {
|
||||
return m_helper.getLayout(title, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShuffleboardLayout getLayout(String title) {
|
||||
return m_helper.getLayout(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexWidget add(String title, Sendable sendable) {
|
||||
return m_helper.add(title, sendable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexWidget add(Sendable sendable) {
|
||||
return m_helper.add(sendable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleWidget add(String title, Object defaultValue) {
|
||||
return m_helper.add(title, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleWidget add(String title, String typeString, Object defaultValue) {
|
||||
return m_helper.add(title, typeString, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<String> addString(String title, Supplier<String> valueSupplier) {
|
||||
return m_helper.addString(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Double> addNumber(String title, DoubleSupplier valueSupplier) {
|
||||
return m_helper.addNumber(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Double> addDouble(String title, DoubleSupplier valueSupplier) {
|
||||
return m_helper.addDouble(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Float> addFloat(String title, FloatSupplier valueSupplier) {
|
||||
return m_helper.addFloat(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Long> addInteger(String title, LongSupplier valueSupplier) {
|
||||
return m_helper.addInteger(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<Boolean> addBoolean(String title, BooleanSupplier valueSupplier) {
|
||||
return m_helper.addBoolean(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<String[]> addStringArray(
|
||||
String title, Supplier<String[]> valueSupplier) {
|
||||
return m_helper.addStringArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<double[]> addDoubleArray(
|
||||
String title, Supplier<double[]> valueSupplier) {
|
||||
return m_helper.addDoubleArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<float[]> addFloatArray(String title, Supplier<float[]> valueSupplier) {
|
||||
return m_helper.addFloatArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<long[]> addIntegerArray(String title, Supplier<long[]> valueSupplier) {
|
||||
return m_helper.addIntegerArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<boolean[]> addBooleanArray(
|
||||
String title, Supplier<boolean[]> valueSupplier) {
|
||||
return m_helper.addBooleanArray(title, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuppliedValueWidget<byte[]> addRaw(
|
||||
String title, String typeString, Supplier<byte[]> valueSupplier) {
|
||||
return m_helper.addRaw(title, typeString, valueSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildInto(NetworkTable parentTable, NetworkTable metaTable) {
|
||||
NetworkTable tabTable = parentTable.getSubTable(m_title);
|
||||
tabTable.getEntry(".type").setString(kSmartDashboardType);
|
||||
tabTable
|
||||
.getEntry(".type")
|
||||
.getTopic()
|
||||
.setProperty("SmartDashboard", "\"" + kSmartDashboardType + "\"");
|
||||
for (ShuffleboardComponent<?> component : m_helper.getComponents()) {
|
||||
component.buildInto(tabTable, metaTable.getSubTable(component.getTitle()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
|
||||
interface ShuffleboardValue {
|
||||
/**
|
||||
* Gets the title of this Shuffleboard value.
|
||||
*
|
||||
* @return The title of this Shuffleboard value.
|
||||
*/
|
||||
String getTitle();
|
||||
|
||||
/**
|
||||
* Builds the entries for this value.
|
||||
*
|
||||
* @param parentTable the table containing all the data for the parent. Values that require a
|
||||
* complex entry or table structure should call {@code parentTable.getSubTable(getTitle())} to
|
||||
* get the table to put data into. Values that only use a single entry should call {@code
|
||||
* parentTable.getEntry(getTitle())} to get that entry.
|
||||
* @param metaTable the table containing all the metadata for this value and its sub-values
|
||||
*/
|
||||
void buildInto(NetworkTable parentTable, NetworkTable metaTable);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* Abstract superclass for widgets.
|
||||
*
|
||||
* <p>This class is package-private to minimize API surface area.
|
||||
*
|
||||
* @param <W> the self type
|
||||
*/
|
||||
abstract class ShuffleboardWidget<W extends ShuffleboardWidget<W>>
|
||||
extends ShuffleboardComponent<W> {
|
||||
ShuffleboardWidget(ShuffleboardContainer parent, String title) {
|
||||
super(parent, title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of widget used to display the data. If not set, the default widget type will be
|
||||
* used.
|
||||
*
|
||||
* @param widgetType the type of the widget used to display the data
|
||||
* @return this widget object
|
||||
* @see BuiltInWidgets
|
||||
*/
|
||||
public final W withWidget(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 third-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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final W withWidget(String widgetType) {
|
||||
setType(widgetType);
|
||||
return (W) this;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.GenericEntry;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
|
||||
/** A Shuffleboard widget that handles a single data point such as a number or string. */
|
||||
public final class SimpleWidget extends ShuffleboardWidget<SimpleWidget> implements AutoCloseable {
|
||||
private String m_typeString = "";
|
||||
private GenericEntry m_entry;
|
||||
|
||||
SimpleWidget(ShuffleboardContainer parent, String title) {
|
||||
super(parent, title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the NetworkTable entry that contains the data for this widget.
|
||||
*
|
||||
* @return The NetworkTable entry that contains the data for this widget.
|
||||
*/
|
||||
public GenericEntry getEntry() {
|
||||
if (m_entry == null) {
|
||||
forceGenerate();
|
||||
}
|
||||
return m_entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the NetworkTable entry that contains the data for this widget.
|
||||
*
|
||||
* @param typeString NetworkTable type string
|
||||
* @return The NetworkTable entry that contains the data for this widget.
|
||||
*/
|
||||
public GenericEntry getEntry(String typeString) {
|
||||
if (m_entry == null) {
|
||||
m_typeString = typeString;
|
||||
forceGenerate();
|
||||
}
|
||||
return m_entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (m_entry != null) {
|
||||
m_entry.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildInto(NetworkTable parentTable, NetworkTable metaTable) {
|
||||
buildMetadata(metaTable);
|
||||
if (m_entry == null) {
|
||||
m_entry = parentTable.getTopic(getTitle()).getGenericEntry(m_typeString);
|
||||
}
|
||||
}
|
||||
|
||||
private void forceGenerate() {
|
||||
ShuffleboardContainer parent = getParent();
|
||||
while (parent instanceof ShuffleboardLayout layout) {
|
||||
parent = layout.getParent();
|
||||
}
|
||||
ShuffleboardTab tab = (ShuffleboardTab) parent;
|
||||
tab.getRoot().update();
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.BooleanPublisher;
|
||||
import edu.wpi.first.networktables.BooleanTopic;
|
||||
import edu.wpi.first.networktables.GenericPublisher;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* A Shuffleboard widget whose value is provided by user code.
|
||||
*
|
||||
* @param <T> the type of values in the widget
|
||||
*/
|
||||
public final class SuppliedValueWidget<T> extends ShuffleboardWidget<SuppliedValueWidget<T>>
|
||||
implements AutoCloseable {
|
||||
private final String m_typeString;
|
||||
private final Supplier<T> m_supplier;
|
||||
private final BiConsumer<GenericPublisher, T> m_setter;
|
||||
private BooleanPublisher m_controllablePub;
|
||||
private GenericPublisher m_entry;
|
||||
|
||||
/**
|
||||
* Package-private constructor for use by the Shuffleboard API.
|
||||
*
|
||||
* @param parent the parent container for the widget
|
||||
* @param title the title of the widget
|
||||
* @param typeString the NetworkTables string type
|
||||
* @param supplier the supplier for values to place in the NetworkTable entry
|
||||
* @param setter the function for placing values in the NetworkTable entry
|
||||
*/
|
||||
SuppliedValueWidget(
|
||||
ShuffleboardContainer parent,
|
||||
String title,
|
||||
String typeString,
|
||||
Supplier<T> supplier,
|
||||
BiConsumer<GenericPublisher, T> setter) {
|
||||
super(parent, title);
|
||||
m_typeString = typeString;
|
||||
m_supplier = supplier;
|
||||
m_setter = setter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildInto(NetworkTable parentTable, NetworkTable metaTable) {
|
||||
buildMetadata(metaTable);
|
||||
if (m_controllablePub == null) {
|
||||
m_controllablePub = new BooleanTopic(metaTable.getTopic("Controllable")).publish();
|
||||
m_controllablePub.set(false);
|
||||
}
|
||||
|
||||
if (m_entry == null) {
|
||||
m_entry = parentTable.getTopic(getTitle()).genericPublish(m_typeString);
|
||||
}
|
||||
m_setter.accept(m_entry, m_supplier.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (m_controllablePub != null) {
|
||||
m_controllablePub.close();
|
||||
}
|
||||
if (m_entry != null) {
|
||||
m_entry.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public interface WidgetType {
|
||||
/**
|
||||
* Gets the string type of the widget as defined by that widget in Shuffleboard.
|
||||
*
|
||||
* @return The string type of the widget.
|
||||
*/
|
||||
String getWidgetName();
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import edu.wpi.first.util.sendable.Sendable;
|
||||
import edu.wpi.first.util.sendable.SendableBuilder;
|
||||
import edu.wpi.first.util.sendable.SendableRegistry;
|
||||
|
||||
/** A mock sendable that marks itself as an actuator. */
|
||||
public class MockActuatorSendable implements Sendable {
|
||||
@SuppressWarnings("this-escape")
|
||||
public MockActuatorSendable(String name) {
|
||||
SendableRegistry.add(this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSendable(SendableBuilder builder) {
|
||||
builder.setActuator(true);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class SendableCameraWrapperTest {
|
||||
NetworkTableInstance m_inst;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
m_inst = NetworkTableInstance.create();
|
||||
SendableCameraWrapper.clearWrappers();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
m_inst.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullCameraName() {
|
||||
assertThrows(NullPointerException.class, () -> SendableCameraWrapper.wrap(null, ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyCameraName() {
|
||||
assertThrows(IllegalArgumentException.class, () -> SendableCameraWrapper.wrap("", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullUrlArray() {
|
||||
assertThrows(
|
||||
NullPointerException.class, () -> SendableCameraWrapper.wrap("name", (String[]) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullUrlInArray() {
|
||||
assertThrows(NullPointerException.class, () -> SendableCameraWrapper.wrap("name", "url", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyUrlArray() {
|
||||
assertThrows(IllegalArgumentException.class, () -> SendableCameraWrapper.wrap("name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUrlsAddedToNt() {
|
||||
SendableCameraWrapper.wrap("name", "url1", "url2");
|
||||
assertArrayEquals(
|
||||
new String[] {"url1", "url2"},
|
||||
NetworkTableInstance.getDefault()
|
||||
.getEntry("/CameraPublisher/name/streams")
|
||||
.getValue()
|
||||
.getStringArray());
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import edu.wpi.first.networktables.GenericEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableEvent.Kind;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.PubSubOption;
|
||||
import edu.wpi.first.networktables.StringSubscriber;
|
||||
import java.util.EnumSet;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ShuffleboardInstanceTest {
|
||||
private NetworkTableInstance m_ntInstance;
|
||||
private ShuffleboardInstance m_shuffleboardInstance;
|
||||
|
||||
@BeforeEach
|
||||
void setupInstance() {
|
||||
m_ntInstance = NetworkTableInstance.create();
|
||||
m_shuffleboardInstance = new ShuffleboardInstance(m_ntInstance);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDownInstance() {
|
||||
m_ntInstance.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPathFluent() {
|
||||
GenericEntry entry =
|
||||
m_shuffleboardInstance
|
||||
.getTab("Tab Title")
|
||||
.getLayout("Layout Title", "List Layout")
|
||||
.add("Data", "string")
|
||||
.withWidget("Text View")
|
||||
.getEntry();
|
||||
|
||||
assertAll(
|
||||
() -> assertEquals("string", entry.getString(null), "Wrong entry value"),
|
||||
() ->
|
||||
assertEquals(
|
||||
"/Shuffleboard/Tab Title/Layout Title/Data",
|
||||
entry.getTopic().getName(),
|
||||
"Entry path generated incorrectly"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNestedLayoutsFluent() {
|
||||
GenericEntry entry =
|
||||
m_shuffleboardInstance
|
||||
.getTab("Tab")
|
||||
.getLayout("First", "List")
|
||||
.getLayout("Second", "List")
|
||||
.getLayout("Third", "List")
|
||||
.getLayout("Fourth", "List")
|
||||
.add("Value", "string")
|
||||
.getEntry();
|
||||
|
||||
assertAll(
|
||||
() -> assertEquals("string", entry.getString(null), "Wrong entry value"),
|
||||
() ->
|
||||
assertEquals(
|
||||
"/Shuffleboard/Tab/First/Second/Third/Fourth/Value",
|
||||
entry.getTopic().getName(),
|
||||
"Entry path generated incorrectly"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNestedLayoutsOop() {
|
||||
ShuffleboardTab tab = m_shuffleboardInstance.getTab("Tab");
|
||||
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");
|
||||
GenericEntry entry = widget.getEntry();
|
||||
|
||||
assertAll(
|
||||
() -> assertEquals("string", entry.getString(null), "Wrong entry value"),
|
||||
() ->
|
||||
assertEquals(
|
||||
"/Shuffleboard/Tab/First/Second/Third/Fourth/Value",
|
||||
entry.getTopic().getName(),
|
||||
"Entry path generated incorrectly"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLayoutTypeIsSet() {
|
||||
String layoutType = "Type";
|
||||
m_shuffleboardInstance.getTab("Tab").getLayout("Title", layoutType);
|
||||
m_shuffleboardInstance.update();
|
||||
NetworkTableEntry entry =
|
||||
m_ntInstance.getEntry("/Shuffleboard/.metadata/Tab/Title/PreferredComponent");
|
||||
assertEquals(layoutType, entry.getString("Not Set"), "Layout type not set");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNestedActuatorWidgetsAreDisabled() {
|
||||
m_shuffleboardInstance
|
||||
.getTab("Tab")
|
||||
.getLayout("Title", "Layout")
|
||||
.add(new MockActuatorSendable("Actuator"));
|
||||
NetworkTableEntry controllableEntry =
|
||||
m_ntInstance.getEntry("/Shuffleboard/Tab/Title/Actuator/.controllable");
|
||||
|
||||
m_shuffleboardInstance.update();
|
||||
|
||||
// Note: we use the unsafe `getBoolean()` method because if the value is NOT a boolean, or if it
|
||||
// is not present, then something has clearly gone very, very wrong
|
||||
boolean controllable = controllableEntry.getValue().getBoolean();
|
||||
|
||||
// Sanity check
|
||||
assertTrue(controllable, "The nested actuator widget should be enabled by default");
|
||||
m_shuffleboardInstance.disableActuatorWidgets();
|
||||
controllable = controllableEntry.getValue().getBoolean();
|
||||
assertFalse(controllable, "The nested actuator widget should have been disabled");
|
||||
}
|
||||
|
||||
@Disabled("Fails often at counter assertion 'expected: <2> but was: <1>'")
|
||||
@Test
|
||||
void testDuplicateSelectTabs() {
|
||||
int listener = 0;
|
||||
AtomicInteger counter = new AtomicInteger();
|
||||
try (StringSubscriber subscriber =
|
||||
m_ntInstance
|
||||
.getStringTopic("/Shuffleboard/.metadata/Selected")
|
||||
.subscribe("", PubSubOption.keepDuplicates(true))) {
|
||||
listener =
|
||||
m_ntInstance.addListener(
|
||||
subscriber,
|
||||
EnumSet.of(Kind.kValueAll, Kind.kImmediate),
|
||||
event -> counter.incrementAndGet());
|
||||
|
||||
// There shouldn't be anything there
|
||||
assertEquals(0, counter.get());
|
||||
|
||||
m_shuffleboardInstance.selectTab("tab1");
|
||||
m_shuffleboardInstance.selectTab("tab1");
|
||||
assertTrue(m_ntInstance.waitForListenerQueue(1.0), "Listener queue timed out!");
|
||||
assertEquals(2, counter.get());
|
||||
|
||||
} finally {
|
||||
m_ntInstance.removeListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
|
||||
import edu.wpi.first.wpilibj.UtilityClassTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ShuffleboardTest extends UtilityClassTest<Shuffleboard> {
|
||||
ShuffleboardTest() {
|
||||
super(Shuffleboard.class);
|
||||
}
|
||||
|
||||
// Most relevant tests are in ShuffleboardTabTest
|
||||
|
||||
@Test
|
||||
void testTabObjectsCached() {
|
||||
ShuffleboardTab tab1 = Shuffleboard.getTab("testTabObjectsCached");
|
||||
ShuffleboardTab tab2 = Shuffleboard.getTab("testTabObjectsCached");
|
||||
assertSame(tab1, tab2, "Tab objects were not cached");
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.shuffleboard;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class SuppliedValueWidgetTest {
|
||||
private NetworkTableInstance m_ntInstance;
|
||||
private ShuffleboardInstance m_instance;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
m_ntInstance = NetworkTableInstance.create();
|
||||
m_instance = new ShuffleboardInstance(m_ntInstance);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
m_ntInstance.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddString() {
|
||||
AtomicInteger count = new AtomicInteger(0);
|
||||
m_instance.getTab("Tab").addString("Title", () -> Integer.toString(count.incrementAndGet()));
|
||||
NetworkTableEntry entry = m_ntInstance.getEntry("/Shuffleboard/Tab/Title");
|
||||
|
||||
m_instance.update();
|
||||
assertEquals("1", entry.getString(null));
|
||||
|
||||
m_instance.update();
|
||||
assertEquals("2", entry.getString(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddDouble() {
|
||||
AtomicInteger num = new AtomicInteger(0);
|
||||
m_instance.getTab("Tab").addNumber("Title", num::incrementAndGet);
|
||||
NetworkTableEntry entry = m_ntInstance.getEntry("/Shuffleboard/Tab/Title");
|
||||
|
||||
m_instance.update();
|
||||
assertEquals(1, entry.getDouble(0));
|
||||
|
||||
m_instance.update();
|
||||
assertEquals(2, entry.getDouble(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddBoolean() {
|
||||
boolean[] bool = {false};
|
||||
m_instance.getTab("Tab").addBoolean("Title", () -> bool[0] = !bool[0]);
|
||||
NetworkTableEntry entry = m_ntInstance.getEntry("/Shuffleboard/Tab/Title");
|
||||
|
||||
m_instance.update();
|
||||
assertTrue(entry.getBoolean(false));
|
||||
|
||||
m_instance.update();
|
||||
assertFalse(entry.getBoolean(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddStringArray() {
|
||||
String[] arr = {"foo", "bar"};
|
||||
m_instance.getTab("Tab").addStringArray("Title", () -> arr);
|
||||
NetworkTableEntry entry = m_ntInstance.getEntry("/Shuffleboard/Tab/Title");
|
||||
|
||||
m_instance.update();
|
||||
assertArrayEquals(arr, entry.getStringArray(new String[0]));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddDoubleArray() {
|
||||
double[] arr = {0, 1};
|
||||
m_instance.getTab("Tab").addDoubleArray("Title", () -> arr);
|
||||
NetworkTableEntry entry = m_ntInstance.getEntry("/Shuffleboard/Tab/Title");
|
||||
|
||||
m_instance.update();
|
||||
assertArrayEquals(arr, entry.getDoubleArray(new double[0]));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddBooleanArray() {
|
||||
boolean[] arr = {true, false};
|
||||
m_instance.getTab("Tab").addBooleanArray("Title", () -> arr);
|
||||
NetworkTableEntry entry = m_ntInstance.getEntry("/Shuffleboard/Tab/Title");
|
||||
|
||||
m_instance.update();
|
||||
assertArrayEquals(arr, entry.getBooleanArray(new boolean[0]));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddRawBytes() {
|
||||
byte[] arr = {0, 1, 2, 3};
|
||||
m_instance.getTab("Tab").addRaw("Title", () -> arr);
|
||||
NetworkTableEntry entry = m_ntInstance.getEntry("/Shuffleboard/Tab/Title");
|
||||
|
||||
m_instance.update();
|
||||
assertArrayEquals(arr, entry.getRaw(new byte[0]));
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,6 @@
|
||||
"tags": [
|
||||
"Hardware",
|
||||
"Joystick",
|
||||
"Shuffleboard",
|
||||
"Pneumatics"
|
||||
],
|
||||
"foldername": "solenoid",
|
||||
@@ -108,8 +107,7 @@
|
||||
"tags": [
|
||||
"Hardware",
|
||||
"Ultrasonic",
|
||||
"SmartDashboard",
|
||||
"Shuffleboard"
|
||||
"SmartDashboard"
|
||||
],
|
||||
"foldername": "ultrasonic",
|
||||
"gradlebase": "java",
|
||||
@@ -308,22 +306,6 @@
|
||||
"mainclass": "Main",
|
||||
"commandversion": 2
|
||||
},
|
||||
{
|
||||
"name": "Shuffleboard",
|
||||
"description": "Present various data via the Shuffleboard API.",
|
||||
"tags": [
|
||||
"Basic Robot",
|
||||
"Differential Drive",
|
||||
"Elevator",
|
||||
"Analog",
|
||||
"Encoder",
|
||||
"Shuffleboard"
|
||||
],
|
||||
"foldername": "shuffleboard",
|
||||
"gradlebase": "java",
|
||||
"mainclass": "Main",
|
||||
"commandversion": 2
|
||||
},
|
||||
{
|
||||
"name": "'Traditional' Hatchbot",
|
||||
"description": "A fully-functional command-based hatchbot for the 2019 game, written in the 'traditional' style, i.e. commands are given their own classes.",
|
||||
@@ -333,7 +315,6 @@
|
||||
"Differential Drive",
|
||||
"Encoder",
|
||||
"Pneumatics",
|
||||
"Shuffleboard",
|
||||
"Sendable",
|
||||
"DataLog",
|
||||
"XboxController"
|
||||
@@ -352,7 +333,6 @@
|
||||
"Differential Drive",
|
||||
"Encoder",
|
||||
"Pneumatics",
|
||||
"Shuffleboard",
|
||||
"Sendable",
|
||||
"DataLog",
|
||||
"PS4Controller"
|
||||
|
||||
@@ -9,11 +9,9 @@ import edu.wpi.first.wpilibj.examples.hatchbotinlined.Constants.OIConstants;
|
||||
import edu.wpi.first.wpilibj.examples.hatchbotinlined.commands.Autos;
|
||||
import edu.wpi.first.wpilibj.examples.hatchbotinlined.subsystems.DriveSubsystem;
|
||||
import edu.wpi.first.wpilibj.examples.hatchbotinlined.subsystems.HatchSubsystem;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.EventImportance;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.CommandScheduler;
|
||||
import edu.wpi.first.wpilibj2.command.Commands;
|
||||
import edu.wpi.first.wpilibj2.command.button.CommandPS4Controller;
|
||||
|
||||
@@ -64,28 +62,11 @@ public class RobotContainer {
|
||||
m_chooser.addOption("Complex Auto", m_complexAuto);
|
||||
|
||||
// Put the chooser on the dashboard
|
||||
Shuffleboard.getTab("Autonomous").add(m_chooser);
|
||||
SmartDashboard.putData("Autonomous", m_chooser);
|
||||
|
||||
// Put subsystems to dashboard.
|
||||
Shuffleboard.getTab("Drivetrain").add(m_robotDrive);
|
||||
Shuffleboard.getTab("HatchSubsystem").add(m_hatchSubsystem);
|
||||
|
||||
// Set the scheduler to log Shuffleboard events for command initialize, interrupt, finish
|
||||
CommandScheduler.getInstance()
|
||||
.onCommandInitialize(
|
||||
command ->
|
||||
Shuffleboard.addEventMarker(
|
||||
"Command initialized", command.getName(), EventImportance.kNormal));
|
||||
CommandScheduler.getInstance()
|
||||
.onCommandInterrupt(
|
||||
command ->
|
||||
Shuffleboard.addEventMarker(
|
||||
"Command interrupted", command.getName(), EventImportance.kNormal));
|
||||
CommandScheduler.getInstance()
|
||||
.onCommandFinish(
|
||||
command ->
|
||||
Shuffleboard.addEventMarker(
|
||||
"Command finished", command.getName(), EventImportance.kNormal));
|
||||
SmartDashboard.putData("Drivetrain", m_robotDrive);
|
||||
SmartDashboard.putData("HatchSubsystem", m_hatchSubsystem);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,11 +17,9 @@ import edu.wpi.first.wpilibj.examples.hatchbottraditional.commands.HalveDriveSpe
|
||||
import edu.wpi.first.wpilibj.examples.hatchbottraditional.commands.ReleaseHatch;
|
||||
import edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems.DriveSubsystem;
|
||||
import edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems.HatchSubsystem;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.EventImportance;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.CommandScheduler;
|
||||
import edu.wpi.first.wpilibj2.command.button.JoystickButton;
|
||||
|
||||
/**
|
||||
@@ -71,32 +69,10 @@ public class RobotContainer {
|
||||
m_chooser.addOption("Complex Auto", m_complexAuto);
|
||||
|
||||
// Put the chooser on the dashboard
|
||||
Shuffleboard.getTab("Autonomous").add(m_chooser);
|
||||
SmartDashboard.putData("Autonomous", m_chooser);
|
||||
// Put subsystems to dashboard.
|
||||
Shuffleboard.getTab("Drivetrain").add(m_robotDrive);
|
||||
Shuffleboard.getTab("HatchSubsystem").add(m_hatchSubsystem);
|
||||
|
||||
// Log Shuffleboard events for command initialize, execute, finish, interrupt
|
||||
CommandScheduler.getInstance()
|
||||
.onCommandInitialize(
|
||||
command ->
|
||||
Shuffleboard.addEventMarker(
|
||||
"Command initialized", command.getName(), EventImportance.kNormal));
|
||||
CommandScheduler.getInstance()
|
||||
.onCommandExecute(
|
||||
command ->
|
||||
Shuffleboard.addEventMarker(
|
||||
"Command executed", command.getName(), EventImportance.kNormal));
|
||||
CommandScheduler.getInstance()
|
||||
.onCommandFinish(
|
||||
command ->
|
||||
Shuffleboard.addEventMarker(
|
||||
"Command finished", command.getName(), EventImportance.kNormal));
|
||||
CommandScheduler.getInstance()
|
||||
.onCommandInterrupt(
|
||||
command ->
|
||||
Shuffleboard.addEventMarker(
|
||||
"Command interrupted", command.getName(), EventImportance.kNormal));
|
||||
SmartDashboard.putData("Drivetrain", m_robotDrive);
|
||||
SmartDashboard.putData("HatchSubsystem", m_hatchSubsystem);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.shuffleboard;
|
||||
|
||||
import edu.wpi.first.wpilibj.RobotBase;
|
||||
|
||||
/**
|
||||
* Do NOT add any static variables to this class, or any initialization at all. Unless you know what
|
||||
* you are doing, do not modify this file except to change the parameter class to the startRobot
|
||||
* call.
|
||||
*/
|
||||
public final class Main {
|
||||
private Main() {}
|
||||
|
||||
/**
|
||||
* Main initialization function. Do not perform any initialization here.
|
||||
*
|
||||
* <p>If you change your main robot class, change the parameter type.
|
||||
*/
|
||||
public static void main(String... args) {
|
||||
RobotBase.startRobot(Robot::new);
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.shuffleboard;
|
||||
|
||||
import edu.wpi.first.networktables.GenericEntry;
|
||||
import edu.wpi.first.util.sendable.SendableRegistry;
|
||||
import edu.wpi.first.wpilibj.AnalogPotentiometer;
|
||||
import edu.wpi.first.wpilibj.Encoder;
|
||||
import edu.wpi.first.wpilibj.TimedRobot;
|
||||
import edu.wpi.first.wpilibj.drive.DifferentialDrive;
|
||||
import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.BuiltInLayouts;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardLayout;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardTab;
|
||||
|
||||
public class Robot extends TimedRobot {
|
||||
private final PWMSparkMax m_leftDriveMotor = new PWMSparkMax(0);
|
||||
private final PWMSparkMax m_rightDriveMotor = new PWMSparkMax(1);
|
||||
private final DifferentialDrive m_tankDrive =
|
||||
new DifferentialDrive(m_leftDriveMotor::set, m_rightDriveMotor::set);
|
||||
private final Encoder m_leftEncoder = new Encoder(0, 1);
|
||||
private final Encoder m_rightEncoder = new Encoder(2, 3);
|
||||
|
||||
private final PWMSparkMax m_elevatorMotor = new PWMSparkMax(2);
|
||||
private final AnalogPotentiometer m_elevatorPot = new AnalogPotentiometer(0);
|
||||
private final GenericEntry m_maxSpeed;
|
||||
|
||||
/** Called once at the beginning of the robot program. */
|
||||
public Robot() {
|
||||
SendableRegistry.addChild(m_tankDrive, m_leftDriveMotor);
|
||||
SendableRegistry.addChild(m_tankDrive, m_rightDriveMotor);
|
||||
|
||||
// Add a 'max speed' widget to a tab named 'Configuration', using a number slider
|
||||
// The widget will be placed in the second column and row and will be TWO columns wide
|
||||
m_maxSpeed =
|
||||
Shuffleboard.getTab("Configuration")
|
||||
.add("Max Speed", 1)
|
||||
.withWidget("Number Slider")
|
||||
.withPosition(1, 1)
|
||||
.withSize(2, 1)
|
||||
.getEntry();
|
||||
|
||||
// Add the tank drive and encoders to a 'Drivebase' tab
|
||||
ShuffleboardTab driveBaseTab = Shuffleboard.getTab("Drivebase");
|
||||
driveBaseTab.add("Tank Drive", m_tankDrive);
|
||||
// Put both encoders in a list layout
|
||||
ShuffleboardLayout encoders =
|
||||
driveBaseTab.getLayout("Encoders", BuiltInLayouts.kList).withPosition(0, 0).withSize(2, 2);
|
||||
encoders.add("Left Encoder", m_leftEncoder);
|
||||
encoders.add("Right Encoder", m_rightEncoder);
|
||||
|
||||
// Add the elevator motor and potentiometer to an 'Elevator' tab
|
||||
ShuffleboardTab elevatorTab = Shuffleboard.getTab("Elevator");
|
||||
elevatorTab.add("Motor", m_elevatorMotor);
|
||||
elevatorTab.add("Potentiometer", m_elevatorPot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void autonomousInit() {
|
||||
// Read the value of the 'max speed' widget from the dashboard
|
||||
m_tankDrive.setMaxOutput(m_maxSpeed.getDouble(1.0));
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,7 @@ import edu.wpi.first.wpilibj.Joystick;
|
||||
import edu.wpi.first.wpilibj.PneumaticsModuleType;
|
||||
import edu.wpi.first.wpilibj.Solenoid;
|
||||
import edu.wpi.first.wpilibj.TimedRobot;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardTab;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
|
||||
|
||||
/**
|
||||
* This is a sample program showing the use of the solenoid classes during operator control. Three
|
||||
@@ -46,29 +45,28 @@ public class Robot extends TimedRobot {
|
||||
|
||||
/** Called once at the beginning of the robot program. */
|
||||
public Robot() {
|
||||
// Publish elements to shuffleboard.
|
||||
ShuffleboardTab tab = Shuffleboard.getTab("Pneumatics");
|
||||
tab.add("Single Solenoid", m_solenoid);
|
||||
tab.add("Double Solenoid", m_doubleSolenoid);
|
||||
tab.add("Compressor", m_compressor);
|
||||
|
||||
// Also publish some raw data
|
||||
// Get the pressure (in PSI) from the analog sensor connected to the PH.
|
||||
// This function is supported only on the PH!
|
||||
// On a PCM, this function will return 0.
|
||||
tab.addDouble("PH Pressure [PSI]", m_compressor::getPressure);
|
||||
// Get compressor current draw.
|
||||
tab.addDouble("Compressor Current", m_compressor::getCurrent);
|
||||
// Get whether the compressor is active.
|
||||
tab.addBoolean("Compressor Active", m_compressor::isEnabled);
|
||||
// Get the digital pressure switch connected to the PCM/PH.
|
||||
// The switch is open when the pressure is over ~120 PSI.
|
||||
tab.addBoolean("Pressure Switch", m_compressor::getPressureSwitchValue);
|
||||
// Publish elements to dashboard.
|
||||
SmartDashboard.putData("Single Solenoid", m_solenoid);
|
||||
SmartDashboard.putData("Double Solenoid", m_doubleSolenoid);
|
||||
SmartDashboard.putData("Compressor", m_compressor);
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.UnconditionalIfStatement")
|
||||
@Override
|
||||
public void teleopPeriodic() {
|
||||
// Publish some raw data
|
||||
// Get the pressure (in PSI) from the analog sensor connected to the PH.
|
||||
// This function is supported only on the PH!
|
||||
// On a PCM, this function will return 0.
|
||||
SmartDashboard.putNumber("PH Pressure [PSI]", m_compressor.getPressure());
|
||||
// Get compressor current draw.
|
||||
SmartDashboard.putNumber("Compressor Current", m_compressor.getCurrent());
|
||||
// Get whether the compressor is active.
|
||||
SmartDashboard.putBoolean("Compressor Active", m_compressor.isEnabled());
|
||||
// Get the digital pressure switch connected to the PCM/PH.
|
||||
// The switch is open when the pressure is over ~120 PSI.
|
||||
SmartDashboard.putBoolean("Pressure Switch", m_compressor.getPressureSwitchValue());
|
||||
|
||||
/*
|
||||
* The output of GetRawButton is true/false depending on whether
|
||||
* the button is pressed; Set takes a boolean for whether
|
||||
|
||||
@@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj.examples.ultrasonic;
|
||||
|
||||
import edu.wpi.first.wpilibj.TimedRobot;
|
||||
import edu.wpi.first.wpilibj.Ultrasonic;
|
||||
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
|
||||
|
||||
/**
|
||||
@@ -21,7 +20,7 @@ public class Robot extends TimedRobot {
|
||||
public Robot() {
|
||||
// Add the ultrasonic on the "Sensors" tab of the dashboard
|
||||
// Data will update automatically
|
||||
Shuffleboard.getTab("Sensors").add(m_rangeFinder);
|
||||
SmartDashboard.putData("Sensors", m_rangeFinder);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user