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:
Peter Johnson
2017-12-04 23:28:33 -08:00
committed by GitHub
parent 3befc7015b
commit f9bece2ffb
213 changed files with 3704 additions and 3758 deletions

View File

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