mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
Update LiveWindow to provide continuous telemetry. (#771)
LiveWindow.updateValues() is now called from IterativeRobotBase on every loop iteration. Telemetry for all WPILib classes is enabled by default; it can be disabled for specific classes using LiveWindow.disableTelemetry(), or all telemetry can be disabled using LiveWindow.disableAllTelemetry(). This necessitated changing the hook methodology into other classes to be more property-based rather than each class providing multiple functions. This had the benefit of reducing boilerplate and increasing consistency. - Remove NamedSendable, add name to Sendable. - Provide SendableBase abstract class. - Deprecate LiveWindow addSensor/addActuator interfaces. - Add LiveWindow support to drive classes. - Add addChild() helper functions to Subsystem. - Fix inheritance hierarchy. Now only sensors inherit from SensorBase. Other devices inherit from some combination of SendableBase, ErrorBase, or nothing.
This commit is contained in:
@@ -9,14 +9,52 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <llvm/DenseMap.h>
|
||||
#include <llvm/SmallString.h>
|
||||
#include <llvm/raw_ostream.h>
|
||||
#include <support/mutex.h>
|
||||
|
||||
#include "Commands/Scheduler.h"
|
||||
#include "SmartDashboard/SendableBuilderImpl.h"
|
||||
#include "networktables/NetworkTable.h"
|
||||
#include "networktables/NetworkTableEntry.h"
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
|
||||
using namespace frc;
|
||||
|
||||
using llvm::Twine;
|
||||
|
||||
struct LiveWindow::Impl {
|
||||
Impl();
|
||||
|
||||
struct Component {
|
||||
std::shared_ptr<Sendable> sendable;
|
||||
Sendable* parent = nullptr;
|
||||
SendableBuilderImpl builder;
|
||||
bool firstTime = true;
|
||||
bool telemetryEnabled = true;
|
||||
};
|
||||
|
||||
wpi::mutex mutex;
|
||||
|
||||
llvm::DenseMap<void*, Component> components;
|
||||
|
||||
std::shared_ptr<nt::NetworkTable> liveWindowTable;
|
||||
std::shared_ptr<nt::NetworkTable> statusTable;
|
||||
nt::NetworkTableEntry enabledEntry;
|
||||
|
||||
bool startLiveWindow = false;
|
||||
bool liveWindowEnabled = false;
|
||||
bool telemetryEnabled = true;
|
||||
};
|
||||
|
||||
LiveWindow::Impl::Impl()
|
||||
: liveWindowTable(
|
||||
nt::NetworkTableInstance::GetDefault().GetTable("LiveWindow")) {
|
||||
statusTable = liveWindowTable->GetSubTable(".status");
|
||||
enabledEntry = statusTable->GetEntry("LW Enabled");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the LiveWindow main class.
|
||||
*
|
||||
@@ -33,11 +71,11 @@ LiveWindow* LiveWindow::GetInstance() {
|
||||
*
|
||||
* Allocate the necessary tables.
|
||||
*/
|
||||
LiveWindow::LiveWindow() : m_scheduler(Scheduler::GetInstance()) {
|
||||
m_liveWindowTable =
|
||||
nt::NetworkTableInstance::GetDefault().GetTable("LiveWindow");
|
||||
m_statusTable = m_liveWindowTable->GetSubTable(".status");
|
||||
m_enabledEntry = m_statusTable->GetEntry("LW Enabled");
|
||||
LiveWindow::LiveWindow() : m_impl(new Impl) {}
|
||||
|
||||
bool LiveWindow::IsEnabled() const {
|
||||
std::lock_guard<wpi::mutex> lock(m_impl->mutex);
|
||||
return m_impl->liveWindowEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,25 +84,21 @@ LiveWindow::LiveWindow() : m_scheduler(Scheduler::GetInstance()) {
|
||||
* If it changes to enabled, start livewindow running otherwise stop it
|
||||
*/
|
||||
void LiveWindow::SetEnabled(bool enabled) {
|
||||
if (m_enabled == enabled) return;
|
||||
std::lock_guard<wpi::mutex> lock(m_impl->mutex);
|
||||
if (m_impl->liveWindowEnabled == enabled) return;
|
||||
Scheduler* scheduler = Scheduler::GetInstance();
|
||||
if (enabled) {
|
||||
if (m_firstTime) {
|
||||
InitializeLiveWindowComponents();
|
||||
m_firstTime = false;
|
||||
}
|
||||
m_scheduler->SetEnabled(false);
|
||||
m_scheduler->RemoveAll();
|
||||
for (auto& elem : m_components) {
|
||||
elem.first->StartLiveWindowMode();
|
||||
}
|
||||
scheduler->SetEnabled(false);
|
||||
scheduler->RemoveAll();
|
||||
} else {
|
||||
for (auto& elem : m_components) {
|
||||
elem.first->StopLiveWindowMode();
|
||||
for (auto& i : m_impl->components) {
|
||||
i.getSecond().builder.StopLiveWindowMode();
|
||||
}
|
||||
m_scheduler->SetEnabled(true);
|
||||
scheduler->SetEnabled(true);
|
||||
}
|
||||
m_enabled = enabled;
|
||||
m_enabledEntry.SetBoolean(m_enabled);
|
||||
m_impl->startLiveWindow = enabled;
|
||||
m_impl->liveWindowEnabled = enabled;
|
||||
m_impl->enabledEntry.SetBoolean(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,41 +108,38 @@ void LiveWindow::SetEnabled(bool enabled) {
|
||||
*
|
||||
* @param subsystem The subsystem this component is part of.
|
||||
* @param name The name of this component.
|
||||
* @param component A LiveWindowSendable component that represents a sensor.
|
||||
* @param component A Sendable component that represents a sensor.
|
||||
*/
|
||||
//@{
|
||||
/**
|
||||
* @brief Use a STL smart pointer to share ownership of component.
|
||||
* @deprecated Use Sendable::SetName() instead.
|
||||
*/
|
||||
void LiveWindow::AddSensor(const std::string& subsystem,
|
||||
const std::string& name,
|
||||
std::shared_ptr<LiveWindowSendable> component) {
|
||||
m_components[component].subsystem = subsystem;
|
||||
m_components[component].name = name;
|
||||
m_components[component].isSensor = true;
|
||||
void LiveWindow::AddSensor(const llvm::Twine& subsystem,
|
||||
const llvm::Twine& name,
|
||||
std::shared_ptr<Sendable> component) {
|
||||
Add(component);
|
||||
component->SetName(subsystem, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pass a reference to LiveWindow and retain ownership of the component.
|
||||
* @deprecated Use Sendable::SetName() instead.
|
||||
*/
|
||||
void LiveWindow::AddSensor(const std::string& subsystem,
|
||||
const std::string& name,
|
||||
LiveWindowSendable& component) {
|
||||
AddSensor(subsystem, name,
|
||||
std::shared_ptr<LiveWindowSendable>(&component,
|
||||
[](LiveWindowSendable*) {}));
|
||||
void LiveWindow::AddSensor(const llvm::Twine& subsystem,
|
||||
const llvm::Twine& name, Sendable& component) {
|
||||
Add(&component);
|
||||
component.SetName(subsystem, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Use a raw pointer to the LiveWindow.
|
||||
* @deprecated Prefer smart pointers or references.
|
||||
* @deprecated Use Sendable::SetName() instead.
|
||||
*/
|
||||
void LiveWindow::AddSensor(const std::string& subsystem,
|
||||
const std::string& name,
|
||||
LiveWindowSendable* component) {
|
||||
AddSensor(subsystem, name,
|
||||
std::shared_ptr<LiveWindowSendable>(
|
||||
component, NullDeleter<LiveWindowSendable>()));
|
||||
void LiveWindow::AddSensor(const llvm::Twine& subsystem,
|
||||
const llvm::Twine& name, Sendable* component) {
|
||||
Add(component);
|
||||
component->SetName(subsystem, name);
|
||||
}
|
||||
//@}
|
||||
|
||||
@@ -116,87 +147,161 @@ void LiveWindow::AddSensor(const std::string& subsystem,
|
||||
* @name AddActuator(subsystem, name, component)
|
||||
*
|
||||
* Add an Actuator associated with the subsystem and call it by the given name.
|
||||
* @deprecated Use Sendable::SetName() instead.
|
||||
*
|
||||
* @param subsystem The subsystem this component is part of.
|
||||
* @param name The name of this component.
|
||||
* @param component A LiveWindowSendable component that represents a actuator.
|
||||
* @param component A Sendable component that represents a actuator.
|
||||
*/
|
||||
//@{
|
||||
/**
|
||||
* @brief Use a STL smart pointer to share ownership of component.
|
||||
*/
|
||||
void LiveWindow::AddActuator(const std::string& subsystem,
|
||||
const std::string& name,
|
||||
std::shared_ptr<LiveWindowSendable> component) {
|
||||
m_components[component].subsystem = subsystem;
|
||||
m_components[component].name = name;
|
||||
m_components[component].isSensor = false;
|
||||
void LiveWindow::AddActuator(const llvm::Twine& subsystem,
|
||||
const llvm::Twine& name,
|
||||
std::shared_ptr<Sendable> component) {
|
||||
Add(component);
|
||||
component->SetName(subsystem, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pass a reference to LiveWindow and retain ownership of the component.
|
||||
* @deprecated Use Sendable::SetName() instead.
|
||||
*/
|
||||
void LiveWindow::AddActuator(const std::string& subsystem,
|
||||
const std::string& name,
|
||||
LiveWindowSendable& component) {
|
||||
AddActuator(subsystem, name,
|
||||
std::shared_ptr<LiveWindowSendable>(&component,
|
||||
[](LiveWindowSendable*) {}));
|
||||
void LiveWindow::AddActuator(const llvm::Twine& subsystem,
|
||||
const llvm::Twine& name, Sendable& component) {
|
||||
Add(&component);
|
||||
component.SetName(subsystem, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Use a raw pointer to the LiveWindow.
|
||||
* @deprecated Prefer smart pointers or references.
|
||||
* @deprecated Use Sendable::SetName() instead.
|
||||
*/
|
||||
void LiveWindow::AddActuator(const std::string& subsystem,
|
||||
const std::string& name,
|
||||
LiveWindowSendable* component) {
|
||||
AddActuator(subsystem, name,
|
||||
std::shared_ptr<LiveWindowSendable>(
|
||||
component, NullDeleter<LiveWindowSendable>()));
|
||||
void LiveWindow::AddActuator(const llvm::Twine& subsystem,
|
||||
const llvm::Twine& name, Sendable* component) {
|
||||
Add(component);
|
||||
component->SetName(subsystem, name);
|
||||
}
|
||||
//@}
|
||||
|
||||
/**
|
||||
* Meant for internal use in other WPILib classes.
|
||||
* @deprecated Use SensorBase::SetName() instead.
|
||||
*/
|
||||
void LiveWindow::AddSensor(std::string type, int channel,
|
||||
LiveWindowSendable* component) {
|
||||
llvm::SmallString<128> buf;
|
||||
llvm::raw_svector_ostream oss(buf);
|
||||
oss << type << "[" << channel << "]";
|
||||
AddSensor("Ungrouped", oss.str(), component);
|
||||
std::shared_ptr<LiveWindowSendable> component_stl(
|
||||
component, NullDeleter<LiveWindowSendable>());
|
||||
if (std::find(m_sensors.begin(), m_sensors.end(), component_stl) ==
|
||||
m_sensors.end())
|
||||
m_sensors.push_back(component_stl);
|
||||
void LiveWindow::AddSensor(const llvm::Twine& type, int channel,
|
||||
Sendable* component) {
|
||||
Add(component);
|
||||
component->SetName("Ungrouped",
|
||||
type + Twine('[') + Twine(channel) + Twine(']'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Meant for internal use in other WPILib classes.
|
||||
* @deprecated Use SensorBase::SetName() instead.
|
||||
*/
|
||||
void LiveWindow::AddActuator(std::string type, int channel,
|
||||
LiveWindowSendable* component) {
|
||||
llvm::SmallString<128> buf;
|
||||
llvm::raw_svector_ostream oss(buf);
|
||||
oss << type << "[" << channel << "]";
|
||||
AddActuator("Ungrouped", oss.str(),
|
||||
std::shared_ptr<LiveWindowSendable>(component,
|
||||
[](LiveWindowSendable*) {}));
|
||||
void LiveWindow::AddActuator(const llvm::Twine& type, int channel,
|
||||
Sendable* component) {
|
||||
Add(component);
|
||||
component->SetName("Ungrouped",
|
||||
type + Twine('[') + Twine(channel) + Twine(']'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Meant for internal use in other WPILib classes.
|
||||
* @deprecated Use SensorBase::SetName() instead.
|
||||
*/
|
||||
void LiveWindow::AddActuator(std::string type, int module, int channel,
|
||||
LiveWindowSendable* component) {
|
||||
llvm::SmallString<128> buf;
|
||||
llvm::raw_svector_ostream oss(buf);
|
||||
oss << type << "[" << module << "," << channel << "]";
|
||||
AddActuator("Ungrouped", oss.str(),
|
||||
std::shared_ptr<LiveWindowSendable>(component,
|
||||
[](LiveWindowSendable*) {}));
|
||||
void LiveWindow::AddActuator(const llvm::Twine& type, int module, int channel,
|
||||
Sendable* component) {
|
||||
Add(component);
|
||||
component->SetName("Ungrouped", type + Twine('[') + Twine(module) +
|
||||
Twine(',') + Twine(channel) + Twine(']'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a component to the LiveWindow.
|
||||
*
|
||||
* @param sendable component to add
|
||||
*/
|
||||
void LiveWindow::Add(std::shared_ptr<Sendable> sendable) {
|
||||
std::lock_guard<wpi::mutex> lock(m_impl->mutex);
|
||||
auto& comp = m_impl->components[sendable.get()];
|
||||
comp.sendable = sendable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a component to the LiveWindow.
|
||||
*
|
||||
* @param sendable component to add
|
||||
*/
|
||||
void LiveWindow::Add(Sendable* sendable) {
|
||||
Add(std::shared_ptr<Sendable>(sendable, NullDeleter<Sendable>()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child component to a component.
|
||||
*
|
||||
* @param parent parent component
|
||||
* @param child child component
|
||||
*/
|
||||
void LiveWindow::AddChild(Sendable* parent, std::shared_ptr<Sendable> child) {
|
||||
AddChild(parent, child.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child component to a component.
|
||||
*
|
||||
* @param parent parent component
|
||||
* @param child child component
|
||||
*/
|
||||
void LiveWindow::AddChild(Sendable* parent, void* child) {
|
||||
std::lock_guard<wpi::mutex> lock(m_impl->mutex);
|
||||
auto& comp = m_impl->components[child];
|
||||
comp.parent = parent;
|
||||
comp.telemetryEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the component from the LiveWindow.
|
||||
*
|
||||
* @param sendable component to remove
|
||||
*/
|
||||
void LiveWindow::Remove(Sendable* sendable) {
|
||||
std::lock_guard<wpi::mutex> lock(m_impl->mutex);
|
||||
m_impl->components.erase(sendable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable telemetry for a single component.
|
||||
*
|
||||
* @param sendable component
|
||||
*/
|
||||
void LiveWindow::EnableTelemetry(Sendable* sendable) {
|
||||
std::lock_guard<wpi::mutex> lock(m_impl->mutex);
|
||||
// Re-enable global setting in case DisableAllTelemetry() was called.
|
||||
m_impl->telemetryEnabled = true;
|
||||
auto i = m_impl->components.find(sendable);
|
||||
if (i != m_impl->components.end()) i->getSecond().telemetryEnabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable telemetry for a single component.
|
||||
*
|
||||
* @param sendable component
|
||||
*/
|
||||
void LiveWindow::DisableTelemetry(Sendable* sendable) {
|
||||
std::lock_guard<wpi::mutex> lock(m_impl->mutex);
|
||||
auto i = m_impl->components.find(sendable);
|
||||
if (i != m_impl->components.end()) i->getSecond().telemetryEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable ALL telemetry.
|
||||
*/
|
||||
void LiveWindow::DisableAllTelemetry() {
|
||||
std::lock_guard<wpi::mutex> lock(m_impl->mutex);
|
||||
m_impl->telemetryEnabled = false;
|
||||
for (auto& i : m_impl->components) i.getSecond().telemetryEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,44 +311,41 @@ void LiveWindow::AddActuator(std::string type, int module, int channel,
|
||||
* SmartDashboard widgets.
|
||||
*/
|
||||
void LiveWindow::UpdateValues() {
|
||||
for (auto& elem : m_sensors) {
|
||||
elem->UpdateTable();
|
||||
}
|
||||
}
|
||||
std::lock_guard<wpi::mutex> lock(m_impl->mutex);
|
||||
// Only do this if either LiveWindow mode or telemetry is enabled.
|
||||
if (!m_impl->liveWindowEnabled && !m_impl->telemetryEnabled) return;
|
||||
|
||||
/**
|
||||
* This method is called periodically to cause the sensors to send new values
|
||||
* to the SmartDashboard.
|
||||
*/
|
||||
void LiveWindow::Run() {
|
||||
if (m_enabled) {
|
||||
UpdateValues();
|
||||
}
|
||||
}
|
||||
for (auto& i : m_impl->components) {
|
||||
auto& comp = i.getSecond();
|
||||
if (comp.sendable && !comp.parent &&
|
||||
(m_impl->liveWindowEnabled || comp.telemetryEnabled)) {
|
||||
if (comp.firstTime) {
|
||||
// By holding off creating the NetworkTable entries, it allows the
|
||||
// components to be redefined. This allows default sensor and actuator
|
||||
// values to be created that are replaced with the custom names from
|
||||
// users calling setName.
|
||||
auto name = comp.sendable->GetName();
|
||||
if (name.empty()) continue;
|
||||
auto subsystem = comp.sendable->GetSubsystem();
|
||||
auto ssTable = m_impl->liveWindowTable->GetSubTable(subsystem);
|
||||
std::shared_ptr<NetworkTable> table;
|
||||
// Treat name==subsystem as top level of subsystem
|
||||
if (name == subsystem)
|
||||
table = ssTable;
|
||||
else
|
||||
table = ssTable->GetSubTable(name);
|
||||
table->GetEntry(".name").SetString(name);
|
||||
comp.builder.SetTable(table);
|
||||
comp.sendable->InitSendable(comp.builder);
|
||||
ssTable->GetEntry(".type").SetString("LW Subsystem");
|
||||
|
||||
/**
|
||||
* Initialize all the LiveWindow elements the first time we enter LiveWindow
|
||||
* mode. By holding off creating the NetworkTable entries, it allows them to be
|
||||
* redefined before the first time in LiveWindow mode. This allows default
|
||||
* sensor and actuator values to be created that are replaced with the custom
|
||||
* names from users calling addActuator and addSensor.
|
||||
*/
|
||||
void LiveWindow::InitializeLiveWindowComponents() {
|
||||
for (auto& elem : m_components) {
|
||||
std::shared_ptr<LiveWindowSendable> component = elem.first;
|
||||
LiveWindowComponent c = elem.second;
|
||||
std::string subsystem = c.subsystem;
|
||||
std::string name = c.name;
|
||||
m_liveWindowTable->GetSubTable(subsystem)->GetEntry(".type").SetString(
|
||||
"LW Subsystem");
|
||||
std::shared_ptr<NetworkTable> table(
|
||||
m_liveWindowTable->GetSubTable(subsystem)->GetSubTable(name));
|
||||
table->GetEntry(".type").SetString(component->GetSmartDashboardType());
|
||||
table->GetEntry(".name").SetString(name);
|
||||
table->GetEntry("Subsystem").SetString(subsystem);
|
||||
component->InitTable(table);
|
||||
if (c.isSensor) {
|
||||
m_sensors.push_back(component);
|
||||
comp.firstTime = false;
|
||||
}
|
||||
|
||||
if (m_impl->startLiveWindow) comp.builder.StartLiveWindowMode();
|
||||
comp.builder.UpdateTable();
|
||||
}
|
||||
}
|
||||
|
||||
m_impl->startLiveWindow = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user