From 5fafaf627275f0c818d81f9d358729f9037e51c5 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sat, 28 Jul 2018 10:43:47 -0700 Subject: [PATCH] Add confirmation to SendableChooser (#1217) This echos back the "selected" value to the "active" key to enable dashboards to display positive feedback to the user that the value is actually set on the robot side. Also fixes SendableChooser so it can be safely added to multiple tables. Changes to "selected" in any table will result in all "active" values being updated. Now that adding SendableChooser to multiple tables is supported, an ".instance" key enables dashboards to treat the same SendableChooser as a common instance if desired. --- .../smartdashboard/SendableChooserBase.cpp | 5 +- .../frc/smartdashboard/SendableChooser.inc | 31 +++++++-- .../frc/smartdashboard/SendableChooserBase.h | 12 +++- .../smartdashboard/SendableChooser.java | 63 +++++++++++++++++-- 4 files changed, 99 insertions(+), 12 deletions(-) diff --git a/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp b/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp index 3b03796b3e..2cdf92cd4e 100644 --- a/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp +++ b/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp @@ -9,4 +9,7 @@ using namespace frc; -SendableChooserBase::SendableChooserBase() : SendableBase(false) {} +std::atomic_int SendableChooserBase::s_instances{0}; + +SendableChooserBase::SendableChooserBase() + : SendableBase(false), m_instance{s_instances++} {} diff --git a/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.inc b/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.inc index 3b9f731a58..fc1e6e721a 100644 --- a/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.inc +++ b/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.inc @@ -61,8 +61,9 @@ template auto SendableChooser::GetSelected() -> decltype(_unwrap_smart_ptr(m_choices[""])) { std::string selected = m_defaultChoice; - if (m_selectedEntry) { - selected = m_selectedEntry.GetString(m_defaultChoice); + { + std::lock_guard lock(m_mutex); + if (m_haveSelected) selected = m_selected; } if (selected.empty()) { return decltype(_unwrap_smart_ptr(m_choices[""])){}; @@ -74,6 +75,7 @@ auto SendableChooser::GetSelected() template void SendableChooser::InitSendable(SendableBuilder& builder) { builder.SetSmartDashboardType("String Chooser"); + builder.GetEntry(kInstance).SetDouble(m_instance); builder.AddStringArrayProperty(kOptions, [=]() { std::vector keys; @@ -90,11 +92,32 @@ void SendableChooser::InitSendable(SendableBuilder& builder) { nullptr); builder.AddSmallStringProperty( kDefault, - [=](const wpi::SmallVectorImpl&) -> wpi::StringRef { + [=](wpi::SmallVectorImpl&) -> wpi::StringRef { return m_defaultChoice; }, nullptr); - m_selectedEntry = builder.GetEntry(kSelected); + builder.AddSmallStringProperty( + kActive, + [=](wpi::SmallVectorImpl& buf) -> wpi::StringRef { + std::lock_guard lock(m_mutex); + if (m_haveSelected) { + buf.assign(m_selected.begin(), m_selected.end()); + return wpi::StringRef(buf.data(), buf.size()); + } else { + return m_defaultChoice; + } + }, + nullptr); + { + std::lock_guard lock(m_mutex); + m_activeEntries.emplace_back(builder.GetEntry(kActive)); + } + builder.AddStringProperty(kSelected, nullptr, [=](wpi::StringRef val) { + std::lock_guard lock(m_mutex); + m_haveSelected = true; + m_selected = val; + for (auto& entry : m_activeEntries) entry.SetString(val); + }); } template diff --git a/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooserBase.h b/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooserBase.h index b77fc02e57..6ae2e6419a 100644 --- a/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooserBase.h +++ b/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooserBase.h @@ -7,9 +7,12 @@ #pragma once +#include #include #include +#include +#include #include "frc/smartdashboard/SendableBase.h" @@ -30,9 +33,16 @@ class SendableChooserBase : public SendableBase { static constexpr const char* kDefault = "default"; static constexpr const char* kOptions = "options"; static constexpr const char* kSelected = "selected"; + static constexpr const char* kActive = "active"; + static constexpr const char* kInstance = ".instance"; std::string m_defaultChoice; - nt::NetworkTableEntry m_selectedEntry; + std::string m_selected; + bool m_haveSelected = false; + wpi::SmallVector m_activeEntries; + wpi::mutex m_mutex; + int m_instance; + static std::atomic_int s_instances; }; } // namespace frc diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableChooser.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableChooser.java index b44c6283c0..b4ce70aca0 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableChooser.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/SendableChooser.java @@ -7,7 +7,11 @@ package edu.wpi.first.wpilibj.smartdashboard; +import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; import edu.wpi.first.networktables.NetworkTableEntry; import edu.wpi.first.wpilibj.SendableBase; @@ -36,22 +40,33 @@ public class SendableChooser extends SendableBase { * The key for the selected option. */ private static final String SELECTED = "selected"; + /** + * The key for the active option. + */ + private static final String ACTIVE = "active"; /** * The key for the option array. */ private static final String OPTIONS = "options"; + /** + * The key for the instance number. + */ + private static final String INSTANCE = ".instance"; /** * A map linking strings to the objects the represent. */ @SuppressWarnings("PMD.LooseCoupling") private final LinkedHashMap m_map = new LinkedHashMap<>(); private String m_defaultChoice = ""; + private final int m_instance; + private static final AtomicInteger s_instances = new AtomicInteger(); /** * Instantiates a {@link SendableChooser}. */ public SendableChooser() { super(false); + m_instance = s_instances.getAndIncrement(); } /** @@ -87,24 +102,60 @@ public class SendableChooser extends SendableBase { * @return the option selected */ public V getSelected() { - String selected = m_defaultChoice; - if (m_tableSelected != null) { - selected = m_tableSelected.getString(m_defaultChoice); + m_mutex.lock(); + try { + if (m_selected != null) { + return m_map.get(m_selected); + } else { + return m_map.get(m_defaultChoice); + } + } finally { + m_mutex.unlock(); } - return m_map.get(selected); } - private NetworkTableEntry m_tableSelected; + private String m_selected; + private final List m_activeEntries = new ArrayList<>(); + private final ReentrantLock m_mutex = new ReentrantLock(); @Override public void initSendable(SendableBuilder builder) { builder.setSmartDashboardType("String Chooser"); + builder.getEntry(INSTANCE).setDouble(m_instance); builder.addStringProperty(DEFAULT, () -> { return m_defaultChoice; }, null); builder.addStringArrayProperty(OPTIONS, () -> { return m_map.keySet().toArray(new String[0]); }, null); - m_tableSelected = builder.getEntry(SELECTED); + builder.addStringProperty(ACTIVE, () -> { + m_mutex.lock(); + try { + if (m_selected != null) { + return m_selected; + } else { + return m_defaultChoice; + } + } finally { + m_mutex.unlock(); + } + }, null); + m_mutex.lock(); + try { + m_activeEntries.add(builder.getEntry(ACTIVE)); + } finally { + m_mutex.unlock(); + } + builder.addStringProperty(SELECTED, null, val -> { + m_mutex.lock(); + try { + m_selected = val; + for (NetworkTableEntry entry : m_activeEntries) { + entry.setString(val); + } + } finally { + m_mutex.unlock(); + } + }); } }