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(); + } + }); } }