[ntcore] NetworkTables 4 (#3217)

This commit is contained in:
Peter Johnson
2022-10-08 10:01:31 -07:00
committed by GitHub
parent 90cfa00115
commit 77301b126c
380 changed files with 34573 additions and 22095 deletions

View File

@@ -10,49 +10,38 @@
using namespace glass;
NTCommandSchedulerModel::NTCommandSchedulerModel(std::string_view path)
: NTCommandSchedulerModel(nt::GetDefaultInstance(), path) {}
: NTCommandSchedulerModel(nt::NetworkTableInstance::GetDefault(), path) {}
NTCommandSchedulerModel::NTCommandSchedulerModel(NT_Inst instance,
NTCommandSchedulerModel::NTCommandSchedulerModel(nt::NetworkTableInstance inst,
std::string_view path)
: m_nt(instance),
m_name(m_nt.GetEntry(fmt::format("{}/.name", path))),
m_commands(m_nt.GetEntry(fmt::format("{}/Names", path))),
m_ids(m_nt.GetEntry(fmt::format("{}/Ids", path))),
m_cancel(m_nt.GetEntry(fmt::format("{}/Cancel", path))),
m_nameValue(wpi::rsplit(path, '/').second) {
m_nt.AddListener(m_name);
m_nt.AddListener(m_commands);
m_nt.AddListener(m_ids);
m_nt.AddListener(m_cancel);
}
: m_inst{inst},
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
m_commands{inst.GetStringArrayTopic(fmt::format("{}/Names", path))
.Subscribe({})},
m_ids{
inst.GetIntegerArrayTopic(fmt::format("{}/Ids", path)).Subscribe({})},
m_cancel{
inst.GetIntegerArrayTopic(fmt::format("{}/Cancel", path)).Publish()},
m_nameValue{wpi::rsplit(path, '/').second} {}
void NTCommandSchedulerModel::CancelCommand(size_t index) {
if (index < m_idsValue.size()) {
nt::SetEntryValue(
m_cancel, nt::NetworkTableValue::MakeDoubleArray({m_idsValue[index]}));
m_cancel.Set({{m_idsValue[index]}});
}
}
void NTCommandSchedulerModel::Update() {
for (auto&& event : m_nt.PollListener()) {
if (event.entry == m_name) {
if (event.value && event.value->IsString()) {
m_nameValue = event.value->GetString();
}
} else if (event.entry == m_commands) {
if (event.value && event.value->IsStringArray()) {
auto arr = event.value->GetStringArray();
m_commandsValue.assign(arr.begin(), arr.end());
}
} else if (event.entry == m_ids) {
if (event.value && event.value->IsDoubleArray()) {
auto arr = event.value->GetDoubleArray();
m_idsValue.assign(arr.begin(), arr.end());
}
}
for (auto&& v : m_name.ReadQueue()) {
m_nameValue = std::move(v.value);
}
for (auto&& v : m_commands.ReadQueue()) {
m_commandsValue = std::move(v.value);
}
for (auto&& v : m_ids.ReadQueue()) {
m_idsValue = std::move(v.value);
}
}
bool NTCommandSchedulerModel::Exists() {
return m_nt.IsConnected() && nt::GetEntryType(m_commands) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_commands.Exists();
}

View File

@@ -10,38 +10,32 @@
using namespace glass;
NTCommandSelectorModel::NTCommandSelectorModel(std::string_view path)
: NTCommandSelectorModel(nt::GetDefaultInstance(), path) {}
: NTCommandSelectorModel(nt::NetworkTableInstance::GetDefault(), path) {}
NTCommandSelectorModel::NTCommandSelectorModel(NT_Inst instance,
NTCommandSelectorModel::NTCommandSelectorModel(nt::NetworkTableInstance inst,
std::string_view path)
: m_nt(instance),
m_running(m_nt.GetEntry(fmt::format("{}/running", path))),
m_name(m_nt.GetEntry(fmt::format("{}/.name", path))),
m_runningData(fmt::format("NTCmd:{}", path)),
m_nameValue(wpi::rsplit(path, '/').second) {
: m_inst{inst},
m_running{inst.GetBooleanTopic(fmt::format("{}/running", path))
.GetEntry(false)},
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
m_runningData{fmt::format("NTCmd:{}", path)},
m_nameValue{wpi::rsplit(path, '/').second} {
m_runningData.SetDigital(true);
m_nt.AddListener(m_running);
m_nt.AddListener(m_name);
}
void NTCommandSelectorModel::SetRunning(bool run) {
nt::SetEntryValue(m_running, nt::NetworkTableValue::MakeBoolean(run));
m_running.Set(run);
}
void NTCommandSelectorModel::Update() {
for (auto&& event : m_nt.PollListener()) {
if (event.entry == m_running) {
if (event.value && event.value->IsBoolean()) {
m_runningData.SetValue(event.value->GetBoolean());
}
} else if (event.entry == m_name) {
if (event.value && event.value->IsString()) {
m_nameValue = event.value->GetString();
}
}
for (auto&& v : m_running.ReadQueue()) {
m_runningData.SetValue(v.value, v.time);
}
for (auto&& v : m_name.ReadQueue()) {
m_nameValue = std::move(v.value);
}
}
bool NTCommandSelectorModel::Exists() {
return m_nt.IsConnected() && nt::GetEntryType(m_running) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_running.Exists();
}

View File

@@ -12,46 +12,40 @@
using namespace glass;
NTDifferentialDriveModel::NTDifferentialDriveModel(std::string_view path)
: NTDifferentialDriveModel(nt::GetDefaultInstance(), path) {}
: NTDifferentialDriveModel(nt::NetworkTableInstance::GetDefault(), path) {}
NTDifferentialDriveModel::NTDifferentialDriveModel(NT_Inst instance,
std::string_view path)
: m_nt(instance),
m_name(m_nt.GetEntry(fmt::format("{}/.name", path))),
m_controllable(m_nt.GetEntry(fmt::format("{}/.controllable", path))),
m_lPercent(m_nt.GetEntry(fmt::format("{}/Left Motor Speed", path))),
m_rPercent(m_nt.GetEntry(fmt::format("{}/Right Motor Speed", path))),
m_nameValue(wpi::rsplit(path, '/').second),
m_lPercentData(fmt::format("NTDiffDriveL:{}", path)),
m_rPercentData(fmt::format("NTDiffDriveR:{}", path)) {
m_nt.AddListener(m_name);
m_nt.AddListener(m_controllable);
m_nt.AddListener(m_lPercent);
m_nt.AddListener(m_rPercent);
NTDifferentialDriveModel::NTDifferentialDriveModel(
nt::NetworkTableInstance inst, std::string_view path)
: m_inst{inst},
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
m_controllable{inst.GetBooleanTopic(fmt::format("{}/.controllable", path))
.Subscribe(false)},
m_lPercent{inst.GetDoubleTopic(fmt::format("{}/Left Motor Speed", path))
.GetEntry(0)},
m_rPercent{inst.GetDoubleTopic(fmt::format("{}/Right Motor Speed", path))
.GetEntry(0)},
m_nameValue{wpi::rsplit(path, '/').second},
m_lPercentData{fmt::format("NTDiffDriveL:{}", path)},
m_rPercentData{fmt::format("NTDiffDriveR:{}", path)} {
m_wheels.emplace_back("L % Output", &m_lPercentData,
[this](auto value) { m_lPercent.Set(value); });
m_wheels.emplace_back("L % Output", &m_lPercentData, [this](auto value) {
nt::SetEntryValue(m_lPercent, nt::NetworkTableValue::MakeDouble(value));
});
m_wheels.emplace_back("R % Output", &m_rPercentData, [this](auto value) {
nt::SetEntryValue(m_rPercent, nt::NetworkTableValue::MakeDouble(value));
});
m_wheels.emplace_back("R % Output", &m_rPercentData,
[this](auto value) { m_rPercent.Set(value); });
}
void NTDifferentialDriveModel::Update() {
for (auto&& event : m_nt.PollListener()) {
if (event.entry == m_name && event.value && event.value->IsString()) {
m_nameValue = event.value->GetString();
} else if (event.entry == m_lPercent && event.value &&
event.value->IsDouble()) {
m_lPercentData.SetValue(event.value->GetDouble());
} else if (event.entry == m_rPercent && event.value &&
event.value->IsDouble()) {
m_rPercentData.SetValue(event.value->GetDouble());
} else if (event.entry == m_controllable && event.value &&
event.value->IsBoolean()) {
m_controllableValue = event.value->GetBoolean();
}
for (auto&& v : m_name.ReadQueue()) {
m_nameValue = std::move(v.value);
}
for (auto&& v : m_lPercent.ReadQueue()) {
m_lPercentData.SetValue(v.value, v.time);
}
for (auto&& v : m_rPercent.ReadQueue()) {
m_rPercentData.SetValue(v.value, v.time);
}
for (auto&& v : m_controllable.ReadQueue()) {
m_controllableValue = v.value;
}
double l = m_lPercentData.GetValue();
@@ -62,5 +56,5 @@ void NTDifferentialDriveModel::Update() {
}
bool NTDifferentialDriveModel::Exists() {
return m_nt.IsConnected() && nt::GetEntryType(m_lPercent) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_lPercent.Exists();
}

View File

@@ -10,34 +10,28 @@
using namespace glass;
NTDigitalInputModel::NTDigitalInputModel(std::string_view path)
: NTDigitalInputModel{nt::GetDefaultInstance(), path} {}
: NTDigitalInputModel{nt::NetworkTableInstance::GetDefault(), path} {}
NTDigitalInputModel::NTDigitalInputModel(NT_Inst inst, std::string_view path)
: m_nt{inst},
m_value{m_nt.GetEntry(fmt::format("{}/Value", path))},
m_name{m_nt.GetEntry(fmt::format("{}/.name", path))},
NTDigitalInputModel::NTDigitalInputModel(nt::NetworkTableInstance inst,
std::string_view path)
: m_inst{inst},
m_value{inst.GetBooleanTopic(fmt::format("{}/Value", path))
.Subscribe(false, {{nt::PubSubOption::SendAll(true)}})},
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
m_valueData{fmt::format("NT_DIn:{}", path)},
m_nameValue{wpi::rsplit(path, '/').second} {
m_nt.AddListener(m_value);
m_nt.AddListener(m_name);
m_valueData.SetDigital(true);
}
void NTDigitalInputModel::Update() {
for (auto&& event : m_nt.PollListener()) {
if (event.entry == m_value) {
if (event.value && event.value->IsBoolean()) {
m_valueData.SetValue(event.value->GetBoolean());
}
} else if (event.entry == m_name) {
if (event.value && event.value->IsString()) {
m_nameValue = event.value->GetString();
}
}
for (auto&& v : m_value.ReadQueue()) {
m_valueData.SetValue(v.value, v.time);
}
for (auto&& v : m_name.ReadQueue()) {
m_nameValue = std::move(v.value);
}
}
bool NTDigitalInputModel::Exists() {
return m_nt.IsConnected() && nt::GetEntryType(m_value) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_value.Exists();
}

View File

@@ -9,43 +9,36 @@
using namespace glass;
NTDigitalOutputModel::NTDigitalOutputModel(std::string_view path)
: NTDigitalOutputModel{nt::GetDefaultInstance(), path} {}
: NTDigitalOutputModel{nt::NetworkTableInstance::GetDefault(), path} {}
NTDigitalOutputModel::NTDigitalOutputModel(NT_Inst inst, std::string_view path)
: m_nt{inst},
m_value{m_nt.GetEntry(fmt::format("{}/Value", path))},
m_name{m_nt.GetEntry(fmt::format("{}/.name", path))},
m_controllable{m_nt.GetEntry(fmt::format("{}/.controllable", path))},
NTDigitalOutputModel::NTDigitalOutputModel(nt::NetworkTableInstance inst,
std::string_view path)
: m_inst{inst},
m_value{inst.GetBooleanTopic(fmt::format("{}/Value", path))
.GetEntry(false, {{nt::PubSubOption::SendAll(true)}})},
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
m_controllable{inst.GetBooleanTopic(fmt::format("{}/.controllable", path))
.Subscribe(false)},
m_valueData{fmt::format("NT_DOut:{}", path)} {
m_nt.AddListener(m_value);
m_nt.AddListener(m_name);
m_nt.AddListener(m_controllable);
m_valueData.SetDigital(true);
}
void NTDigitalOutputModel::SetValue(bool val) {
nt::SetEntryValue(m_value, nt::Value::MakeBoolean(val));
m_value.Set(val);
}
void NTDigitalOutputModel::Update() {
for (auto&& event : m_nt.PollListener()) {
if (event.entry == m_value) {
if (event.value && event.value->IsBoolean()) {
m_valueData.SetValue(event.value->GetBoolean());
}
} else if (event.entry == m_name) {
if (event.value && event.value->IsString()) {
m_nameValue = event.value->GetString();
}
} else if (event.entry == m_controllable) {
if (event.value && event.value->IsBoolean()) {
m_controllableValue = event.value->GetBoolean();
}
}
for (auto&& v : m_value.ReadQueue()) {
m_valueData.SetValue(v.value, v.time);
}
for (auto&& v : m_name.ReadQueue()) {
m_nameValue = std::move(v.value);
}
for (auto&& v : m_controllable.ReadQueue()) {
m_controllableValue = v.value;
}
}
bool NTDigitalOutputModel::Exists() {
return m_nt.IsConnected() && nt::GetEntryType(m_value) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_value.Exists();
}

View File

@@ -13,15 +13,19 @@
using namespace glass;
NTFMSModel::NTFMSModel(std::string_view path)
: NTFMSModel{nt::GetDefaultInstance(), path} {}
: NTFMSModel{nt::NetworkTableInstance::GetDefault(), path} {}
NTFMSModel::NTFMSModel(NT_Inst inst, std::string_view path)
: m_nt{inst},
NTFMSModel::NTFMSModel(nt::NetworkTableInstance inst, std::string_view path)
: m_inst{inst},
m_gameSpecificMessage{
m_nt.GetEntry(fmt::format("{}/GameSpecificMessage", path))},
m_alliance{m_nt.GetEntry(fmt::format("{}/IsRedAlliance", path))},
m_station{m_nt.GetEntry(fmt::format("{}/StationNumber", path))},
m_controlWord{m_nt.GetEntry(fmt::format("{}/FMSControlData", path))},
inst.GetStringTopic(fmt::format("{}/GameSpecificMessage", path))
.Subscribe("")},
m_alliance{inst.GetBooleanTopic(fmt::format("{}/IsRedAlliance", path))
.Subscribe(false, {{nt::PubSubOption::SendAll(true)}})},
m_station{inst.GetIntegerTopic(fmt::format("{}/StationNumber", path))
.Subscribe(0, {{nt::PubSubOption::SendAll(true)}})},
m_controlWord{inst.GetIntegerTopic(fmt::format("{}/FMSControlData", path))
.Subscribe(0, {{nt::PubSubOption::SendAll(true)}})},
m_fmsAttached{fmt::format("NT_FMS:FMSAttached:{}", path)},
m_dsAttached{fmt::format("NT_FMS:DSAttached:{}", path)},
m_allianceStationId{fmt::format("NT_FMS:AllianceStationID:{}", path)},
@@ -29,10 +33,6 @@ NTFMSModel::NTFMSModel(NT_Inst inst, std::string_view path)
m_enabled{fmt::format("NT_FMS:RobotEnabled:{}", path)},
m_test{fmt::format("NT_FMS:TestMode:{}", path)},
m_autonomous{fmt::format("NT_FMS:AutonomousMode:{}", path)} {
m_nt.AddListener(m_alliance);
m_nt.AddListener(m_station);
m_nt.AddListener(m_controlWord);
m_fmsAttached.SetDigital(true);
m_dsAttached.SetDigital(true);
m_estop.SetDigital(true);
@@ -43,49 +43,35 @@ NTFMSModel::NTFMSModel(NT_Inst inst, std::string_view path)
std::string_view NTFMSModel::GetGameSpecificMessage(
wpi::SmallVectorImpl<char>& buf) {
buf.clear();
auto value = nt::GetEntryValue(m_gameSpecificMessage);
if (value && value->IsString()) {
auto str = value->GetString();
buf.append(str.begin(), str.end());
}
return std::string_view{buf.data(), buf.size()};
return m_gameSpecificMessage.Get(buf, "");
}
void NTFMSModel::Update() {
for (auto&& event : m_nt.PollListener()) {
if (event.entry == m_alliance) {
if (event.value && event.value->IsBoolean()) {
int allianceStationId = m_allianceStationId.GetValue();
allianceStationId %= 3;
// true if red
allianceStationId += 3 * (event.value->GetBoolean() ? 0 : 1);
m_allianceStationId.SetValue(allianceStationId);
}
} else if (event.entry == m_station) {
if (event.value && event.value->IsDouble()) {
int allianceStationId = m_allianceStationId.GetValue();
bool isRed = (allianceStationId < 3);
// the NT value is 1-indexed
m_allianceStationId.SetValue(event.value->GetDouble() - 1 +
3 * (isRed ? 0 : 1));
}
} else if (event.entry == m_controlWord) {
if (event.value && event.value->IsDouble()) {
uint32_t controlWord = event.value->GetDouble();
// See HAL_ControlWord definition
auto time = wpi::Now();
m_enabled.SetValue(((controlWord & 0x01) != 0) ? 1 : 0, time);
m_autonomous.SetValue(((controlWord & 0x02) != 0) ? 1 : 0, time);
m_test.SetValue(((controlWord & 0x04) != 0) ? 1 : 0, time);
m_estop.SetValue(((controlWord & 0x08) != 0) ? 1 : 0, time);
m_fmsAttached.SetValue(((controlWord & 0x10) != 0) ? 1 : 0, time);
m_dsAttached.SetValue(((controlWord & 0x20) != 0) ? 1 : 0, time);
}
}
for (auto&& v : m_alliance.ReadQueue()) {
int allianceStationId = m_allianceStationId.GetValue();
allianceStationId %= 3;
// true if red
allianceStationId += 3 * (v.value ? 0 : 1);
m_allianceStationId.SetValue(allianceStationId, v.time);
}
for (auto&& v : m_station.ReadQueue()) {
int allianceStationId = m_allianceStationId.GetValue();
bool isRed = (allianceStationId < 3);
// the NT value is 1-indexed
m_allianceStationId.SetValue(v.value - 1 + 3 * (isRed ? 0 : 1), v.time);
}
for (auto&& v : m_controlWord.ReadQueue()) {
uint32_t controlWord = v.value;
// See HAL_ControlWord definition
m_enabled.SetValue(((controlWord & 0x01) != 0) ? 1 : 0, v.time);
m_autonomous.SetValue(((controlWord & 0x02) != 0) ? 1 : 0, v.time);
m_test.SetValue(((controlWord & 0x04) != 0) ? 1 : 0, v.time);
m_estop.SetValue(((controlWord & 0x08) != 0) ? 1 : 0, v.time);
m_fmsAttached.SetValue(((controlWord & 0x10) != 0) ? 1 : 0, v.time);
m_dsAttached.SetValue(((controlWord & 0x20) != 0) ? 1 : 0, v.time);
}
}
bool NTFMSModel::Exists() {
return m_nt.IsConnected() && nt::GetEntryType(m_controlWord) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_controlWord.Exists();
}

View File

@@ -8,6 +8,8 @@
#include <vector>
#include <fmt/format.h>
#include <networktables/DoubleArrayTopic.h>
#include <networktables/MultiSubscriber.h>
#include <ntcore_cpp.h>
#include <wpi/Endian.h>
#include <wpi/MathExtras.h>
@@ -18,20 +20,16 @@ using namespace glass;
class NTField2DModel::ObjectModel : public FieldObjectModel {
public:
ObjectModel(std::string_view name, NT_Entry entry)
: m_name{name}, m_entry{entry} {}
ObjectModel(std::string_view name, nt::DoubleArrayTopic topic)
: m_name{name}, m_topic{topic} {}
const char* GetName() const override { return m_name.c_str(); }
NT_Entry GetEntry() const { return m_entry; }
nt::DoubleArrayTopic GetTopic() const { return m_topic; }
void NTUpdate(const nt::Value& value);
void Update() override {
if (auto value = nt::GetEntryValue(m_entry)) {
NTUpdate(*value);
}
}
bool Exists() override { return nt::GetEntryType(m_entry) != NT_UNASSIGNED; }
void Update() override {}
bool Exists() override { return m_topic.Exists(); }
bool IsReadOnly() override { return false; }
wpi::span<const frc::Pose2d> GetPoses() override { return m_poses; }
@@ -44,7 +42,8 @@ class NTField2DModel::ObjectModel : public FieldObjectModel {
void UpdateNT();
std::string m_name;
NT_Entry m_entry;
nt::DoubleArrayTopic m_topic;
nt::DoubleArrayPublisher m_pub;
std::vector<frc::Pose2d> m_poses;
};
@@ -62,63 +61,21 @@ void NTField2DModel::ObjectModel::NTUpdate(const nt::Value& value) {
units::meter_t{arr[i * 3 + 0]}, units::meter_t{arr[i * 3 + 1]},
frc::Rotation2d{units::degree_t{arr[i * 3 + 2]}}};
}
} else if (value.IsRaw()) {
// treat it simply as an array of doubles
std::string_view data = value.GetRaw();
// must be triples of doubles
auto size = data.size();
if ((size % (3 * 8)) != 0) {
return;
}
m_poses.resize(size / (3 * 8));
const char* p = data.data();
for (size_t i = 0; i < size / (3 * 8); ++i) {
double x = wpi::BitsToDouble(
wpi::support::endian::readNext<uint64_t, wpi::support::big,
wpi::support::unaligned>(p));
double y = wpi::BitsToDouble(
wpi::support::endian::readNext<uint64_t, wpi::support::big,
wpi::support::unaligned>(p));
double rot = wpi::BitsToDouble(
wpi::support::endian::readNext<uint64_t, wpi::support::big,
wpi::support::unaligned>(p));
m_poses[i] = frc::Pose2d{units::meter_t{x}, units::meter_t{y},
frc::Rotation2d{units::degree_t{rot}}};
}
}
}
void NTField2DModel::ObjectModel::UpdateNT() {
if (m_poses.size() < (255 / 3)) {
wpi::SmallVector<double, 9> arr;
for (auto&& pose : m_poses) {
auto& translation = pose.Translation();
arr.push_back(translation.X().value());
arr.push_back(translation.Y().value());
arr.push_back(pose.Rotation().Degrees().value());
}
nt::SetEntryTypeValue(m_entry, nt::Value::MakeDoubleArray(arr));
} else {
// send as raw array of doubles if too big for NT array
std::vector<char> arr;
arr.resize(m_poses.size() * 3 * 8);
char* p = arr.data();
for (auto&& pose : m_poses) {
auto& translation = pose.Translation();
wpi::support::endian::write64be(
p, wpi::DoubleToBits(translation.X().value()));
p += 8;
wpi::support::endian::write64be(
p, wpi::DoubleToBits(translation.Y().value()));
p += 8;
wpi::support::endian::write64be(
p, wpi::DoubleToBits(pose.Rotation().Degrees().value()));
p += 8;
}
nt::SetEntryTypeValue(m_entry,
nt::Value::MakeRaw({arr.data(), arr.size()}));
wpi::SmallVector<double, 9> arr;
for (auto&& pose : m_poses) {
auto& translation = pose.Translation();
arr.push_back(translation.X().value());
arr.push_back(translation.Y().value());
arr.push_back(pose.Rotation().Degrees().value());
}
if (!m_pub) {
m_pub = m_topic.Publish();
}
m_pub.Set(arr);
}
void NTField2DModel::ObjectModel::SetPoses(wpi::span<const frc::Pose2d> poses) {
@@ -149,69 +106,75 @@ void NTField2DModel::ObjectModel::SetRotation(size_t i, frc::Rotation2d rot) {
}
NTField2DModel::NTField2DModel(std::string_view path)
: NTField2DModel{nt::GetDefaultInstance(), path} {}
: NTField2DModel{nt::NetworkTableInstance::GetDefault(), path} {}
NTField2DModel::NTField2DModel(NT_Inst inst, std::string_view path)
: m_nt{inst},
m_path{fmt::format("{}/", path)},
m_name{m_nt.GetEntry(fmt::format("{}/.name", path))} {
m_nt.AddListener(m_path, NT_NOTIFY_LOCAL | NT_NOTIFY_NEW | NT_NOTIFY_DELETE |
NT_NOTIFY_UPDATE | NT_NOTIFY_IMMEDIATE);
NTField2DModel::NTField2DModel(nt::NetworkTableInstance inst,
std::string_view path)
: m_path{fmt::format("{}/", path)},
m_inst{inst},
m_tableSub{inst,
{{m_path}},
{{nt::PubSubOption::SendAll(true),
nt::PubSubOption::Periodic(0.05)}}},
m_nameTopic{inst.GetTopic(fmt::format("{}/.name", path))},
m_topicListener{inst},
m_valueListener{inst} {
m_topicListener.Add(m_tableSub, NT_TOPIC_NOTIFY_PUBLISH |
NT_TOPIC_NOTIFY_UNPUBLISH |
NT_TOPIC_NOTIFY_IMMEDIATE);
m_valueListener.Add(m_tableSub,
NT_VALUE_NOTIFY_IMMEDIATE | NT_VALUE_NOTIFY_LOCAL);
}
NTField2DModel::~NTField2DModel() = default;
void NTField2DModel::Update() {
for (auto&& event : m_nt.PollListener()) {
// handle publish/unpublish
for (auto&& event : m_topicListener.ReadQueue()) {
auto name = wpi::drop_front(event.info.name, m_path.size());
if (name.empty() || name[0] == '.') {
continue;
}
auto [it, match] = Find(event.info.name);
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
if (match) {
m_objects.erase(it);
}
continue;
} else if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
if (!match) {
it = m_objects.emplace(
it, std::make_unique<ObjectModel>(
event.info.name, nt::DoubleArrayTopic{event.info.topic}));
}
} else if (!match) {
continue;
}
}
// update values
for (auto&& event : m_valueListener.ReadQueue()) {
// .name
if (event.entry == m_name) {
if (event.value && event.value->IsString()) {
m_nameValue = event.value->GetString();
if (event.topic == m_nameTopic.GetHandle()) {
if (event.value && event.value.IsString()) {
m_nameValue = event.value.GetString();
}
continue;
}
// common case: update of existing entry; search by entry
if (event.flags & NT_NOTIFY_UPDATE) {
auto it = std::find_if(
m_objects.begin(), m_objects.end(),
[&](const auto& e) { return e->GetEntry() == event.entry; });
if (it != m_objects.end()) {
(*it)->NTUpdate(*event.value);
continue;
}
}
// handle create/delete
std::string_view name = event.name;
if (wpi::starts_with(name, m_path)) {
name.remove_prefix(m_path.size());
if (name.empty() || name[0] == '.') {
continue;
}
auto [it, match] = Find(event.name);
if (event.flags & NT_NOTIFY_DELETE) {
if (match) {
m_objects.erase(it);
}
continue;
} else if (event.flags & NT_NOTIFY_NEW) {
if (!match) {
it = m_objects.emplace(
it, std::make_unique<ObjectModel>(event.name, event.entry));
}
} else if (!match) {
continue;
}
if (event.flags & (NT_NOTIFY_NEW | NT_NOTIFY_UPDATE)) {
(*it)->NTUpdate(*event.value);
}
auto it =
std::find_if(m_objects.begin(), m_objects.end(), [&](const auto& e) {
return e->GetTopic().GetHandle() == event.topic;
});
if (it != m_objects.end()) {
(*it)->NTUpdate(event.value);
continue;
}
}
}
bool NTField2DModel::Exists() {
return m_nt.IsConnected() && nt::GetEntryType(m_name) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_nameTopic.Exists();
}
bool NTField2DModel::IsReadOnly() {
@@ -222,8 +185,9 @@ FieldObjectModel* NTField2DModel::AddFieldObject(std::string_view name) {
auto fullName = fmt::format("{}{}", m_path, name);
auto [it, match] = Find(fullName);
if (!match) {
it = m_objects.emplace(
it, std::make_unique<ObjectModel>(fullName, m_nt.GetEntry(fullName)));
it = m_objects.emplace(it,
std::make_unique<ObjectModel>(
fullName, m_inst.GetDoubleArrayTopic(fullName)));
}
return it->get();
}
@@ -231,7 +195,6 @@ FieldObjectModel* NTField2DModel::AddFieldObject(std::string_view name) {
void NTField2DModel::RemoveFieldObject(std::string_view name) {
auto [it, match] = Find(fmt::format("{}{}", m_path, name));
if (match) {
nt::DeleteEntry((*it)->GetEntry());
m_objects.erase(it);
}
}

View File

@@ -10,32 +10,25 @@
using namespace glass;
NTGyroModel::NTGyroModel(std::string_view path)
: NTGyroModel(nt::GetDefaultInstance(), path) {}
: NTGyroModel(nt::NetworkTableInstance::GetDefault(), path) {}
NTGyroModel::NTGyroModel(NT_Inst instance, std::string_view path)
: m_nt(instance),
m_angle(m_nt.GetEntry(fmt::format("{}/Value", path))),
m_name(m_nt.GetEntry(fmt::format("{}/.name", path))),
m_angleData(fmt::format("NT_Gyro:{}", path)),
m_nameValue(wpi::rsplit(path, '/').second) {
m_nt.AddListener(m_angle);
m_nt.AddListener(m_name);
}
NTGyroModel::NTGyroModel(nt::NetworkTableInstance inst, std::string_view path)
: m_inst{inst},
m_angle{inst.GetDoubleTopic(fmt::format("{}/Value", path))
.Subscribe(0, {{nt::PubSubOption::SendAll(true)}})},
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe({})},
m_angleData{fmt::format("NT_Gyro:{}", path)},
m_nameValue{wpi::rsplit(path, '/').second} {}
void NTGyroModel::Update() {
for (auto&& event : m_nt.PollListener()) {
if (event.entry == m_angle) {
if (event.value && event.value->IsDouble()) {
m_angleData.SetValue(event.value->GetDouble());
}
} else if (event.entry == m_name) {
if (event.value && event.value->IsString()) {
m_nameValue = event.value->GetString();
}
}
for (auto&& v : m_name.ReadQueue()) {
m_nameValue = std::move(v.value);
}
for (auto&& v : m_angle.ReadQueue()) {
m_angleData.SetValue(v.value, v.time);
}
}
bool NTGyroModel::Exists() {
return m_nt.IsConnected() && nt::GetEntryType(m_angle) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_angle.Exists();
}

View File

@@ -12,69 +12,62 @@
using namespace glass;
NTMecanumDriveModel::NTMecanumDriveModel(std::string_view path)
: NTMecanumDriveModel(nt::GetDefaultInstance(), path) {}
: NTMecanumDriveModel(nt::NetworkTableInstance::GetDefault(), path) {}
NTMecanumDriveModel::NTMecanumDriveModel(NT_Inst instance,
NTMecanumDriveModel::NTMecanumDriveModel(nt::NetworkTableInstance inst,
std::string_view path)
: m_nt(instance),
m_name(m_nt.GetEntry(fmt::format("{}/.name", path))),
m_controllable(m_nt.GetEntry(fmt::format("{}/.controllable", path))),
m_flPercent(
m_nt.GetEntry(fmt::format("{}/Front Left Motor Speed", path))),
m_frPercent(
m_nt.GetEntry(fmt::format("{}/Front Right Motor Speed", path))),
m_rlPercent(m_nt.GetEntry(fmt::format("{}/Rear Left Motor Speed", path))),
m_rrPercent(
m_nt.GetEntry(fmt::format("{}/Rear Right Motor Speed", path))),
m_nameValue(wpi::rsplit(path, '/').second),
m_flPercentData(fmt::format("NTMcnmDriveFL:{}", path)),
m_frPercentData(fmt::format("NTMcnmDriveFR:{}", path)),
m_rlPercentData(fmt::format("NTMcnmDriveRL:{}", path)),
m_rrPercentData(fmt::format("NTMcnmDriveRR:{}", path)) {
m_nt.AddListener(m_name);
m_nt.AddListener(m_controllable);
m_nt.AddListener(m_flPercent);
m_nt.AddListener(m_frPercent);
m_nt.AddListener(m_rlPercent);
m_nt.AddListener(m_rrPercent);
: m_inst{inst},
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
m_controllable{inst.GetBooleanTopic(fmt::format("{}/.controllable", path))
.Subscribe(0)},
m_flPercent{
inst.GetDoubleTopic(fmt::format("{}/Front Left Motor Speed", path))
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
m_frPercent{
inst.GetDoubleTopic(fmt::format("{}/Front Right Motor Speed", path))
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
m_rlPercent{
inst.GetDoubleTopic(fmt::format("{}/Rear Left Motor Speed", path))
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
m_rrPercent{
inst.GetDoubleTopic(fmt::format("{}/Rear Right Motor Speed", path))
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
m_nameValue{wpi::rsplit(path, '/').second},
m_flPercentData{fmt::format("NTMcnmDriveFL:{}", path)},
m_frPercentData{fmt::format("NTMcnmDriveFR:{}", path)},
m_rlPercentData{fmt::format("NTMcnmDriveRL:{}", path)},
m_rrPercentData{fmt::format("NTMcnmDriveRR:{}", path)} {
m_wheels.emplace_back("FL % Output", &m_flPercentData,
[this](auto value) { m_flPercent.Set(value); });
m_wheels.emplace_back("FL % Output", &m_flPercentData, [this](auto value) {
nt::SetEntryValue(m_flPercent, nt::NetworkTableValue::MakeDouble(value));
});
m_wheels.emplace_back("FR % Output", &m_frPercentData,
[this](auto value) { m_frPercent.Set(value); });
m_wheels.emplace_back("FR % Output", &m_frPercentData, [this](auto value) {
nt::SetEntryValue(m_frPercent, nt::NetworkTableValue::MakeDouble(value));
});
m_wheels.emplace_back("RL % Output", &m_rlPercentData,
[this](auto value) { m_rlPercent.Set(value); });
m_wheels.emplace_back("RL % Output", &m_rlPercentData, [this](auto value) {
nt::SetEntryValue(m_rlPercent, nt::NetworkTableValue::MakeDouble(value));
});
m_wheels.emplace_back("RR % Output", &m_rrPercentData, [this](auto value) {
nt::SetEntryValue(m_rrPercent, nt::NetworkTableValue::MakeDouble(value));
});
m_wheels.emplace_back("RR % Output", &m_rrPercentData,
[this](auto value) { m_rrPercent.Set(value); });
}
void NTMecanumDriveModel::Update() {
for (auto&& event : m_nt.PollListener()) {
if (event.entry == m_name && event.value && event.value->IsString()) {
m_nameValue = event.value->GetString();
} else if (event.entry == m_flPercent && event.value &&
event.value->IsDouble()) {
m_flPercentData.SetValue(event.value->GetDouble());
} else if (event.entry == m_frPercent && event.value &&
event.value->IsDouble()) {
m_frPercentData.SetValue(event.value->GetDouble());
} else if (event.entry == m_rlPercent && event.value &&
event.value->IsDouble()) {
m_rlPercentData.SetValue(event.value->GetDouble());
} else if (event.entry == m_rrPercent && event.value &&
event.value->IsDouble()) {
m_rrPercentData.SetValue(event.value->GetDouble());
} else if (event.entry == m_controllable && event.value &&
event.value->IsBoolean()) {
m_controllableValue = event.value->GetBoolean();
}
for (auto&& v : m_name.ReadQueue()) {
m_nameValue = std::move(v.value);
}
for (auto&& v : m_flPercent.ReadQueue()) {
m_flPercentData.SetValue(v.value, v.time);
}
for (auto&& v : m_frPercent.ReadQueue()) {
m_frPercentData.SetValue(v.value, v.time);
}
for (auto&& v : m_rlPercent.ReadQueue()) {
m_rlPercentData.SetValue(v.value, v.time);
}
for (auto&& v : m_rrPercent.ReadQueue()) {
m_rrPercentData.SetValue(v.value, v.time);
}
for (auto&& v : m_controllable.ReadQueue()) {
m_controllableValue = v.value;
}
double fl = m_flPercentData.GetValue();
@@ -88,5 +81,5 @@ void NTMecanumDriveModel::Update() {
}
bool NTMecanumDriveModel::Exists() {
return m_nt.IsConnected() && nt::GetEntryType(m_flPercent) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_flPercent.Exists();
}

View File

@@ -34,16 +34,18 @@ class NTMechanismObjectModel;
class NTMechanismGroupImpl final {
public:
NTMechanismGroupImpl(NT_Inst inst, std::string_view path,
NTMechanismGroupImpl(nt::NetworkTableInstance inst, std::string_view path,
std::string_view name)
: m_inst{inst}, m_path{path}, m_name{name} {}
const char* GetName() const { return m_name.c_str(); }
void ForEachObject(wpi::function_ref<void(MechanismObjectModel& model)> func);
void NTUpdate(const nt::EntryNotification& event, std::string_view name);
void NTUpdate(const nt::TopicNotification& event, std::string_view name);
void NTUpdate(const nt::ValueNotification& event, std::string_view name);
protected:
NT_Inst m_inst;
nt::NetworkTableInstance m_inst;
std::string m_path;
std::string m_name;
std::vector<std::unique_ptr<NTMechanismObjectModel>> m_objects;
@@ -51,14 +53,14 @@ class NTMechanismGroupImpl final {
class NTMechanismObjectModel final : public MechanismObjectModel {
public:
NTMechanismObjectModel(NT_Inst inst, std::string_view path,
NTMechanismObjectModel(nt::NetworkTableInstance inst, std::string_view path,
std::string_view name)
: m_group{inst, path, name},
m_type{nt::GetEntry(inst, fmt::format("{}/.type", path))},
m_color{nt::GetEntry(inst, fmt::format("{}/color", path))},
m_weight{nt::GetEntry(inst, fmt::format("{}/weight", path))},
m_angle{nt::GetEntry(inst, fmt::format("{}/angle", path))},
m_length{nt::GetEntry(inst, fmt::format("{}/length", path))} {}
m_typeTopic{inst.GetTopic(fmt::format("{}/.type", path))},
m_colorTopic{inst.GetTopic(fmt::format("{}/color", path))},
m_weightTopic{inst.GetTopic(fmt::format("{}/weight", path))},
m_angleTopic{inst.GetTopic(fmt::format("{}/angle", path))},
m_lengthTopic{inst.GetTopic(fmt::format("{}/length", path))} {}
const char* GetName() const final { return m_group.GetName(); }
void ForEachObject(
@@ -72,16 +74,17 @@ class NTMechanismObjectModel final : public MechanismObjectModel {
frc::Rotation2d GetAngle() const final { return m_angleValue; }
units::meter_t GetLength() const final { return m_lengthValue; }
bool NTUpdate(const nt::EntryNotification& event, std::string_view childName);
bool NTUpdate(const nt::TopicNotification& event, std::string_view name);
void NTUpdate(const nt::ValueNotification& event, std::string_view name);
private:
NTMechanismGroupImpl m_group;
NT_Entry m_type;
NT_Entry m_color;
NT_Entry m_weight;
NT_Entry m_angle;
NT_Entry m_length;
nt::Topic m_typeTopic;
nt::Topic m_colorTopic;
nt::Topic m_weightTopic;
nt::Topic m_angleTopic;
nt::Topic m_lengthTopic;
std::string m_typeValue;
ImU32 m_colorValue = IM_COL32_WHITE;
@@ -99,7 +102,7 @@ void NTMechanismGroupImpl::ForEachObject(
}
}
void NTMechanismGroupImpl::NTUpdate(const nt::EntryNotification& event,
void NTMechanismGroupImpl::NTUpdate(const nt::TopicNotification& event,
std::string_view name) {
if (name.empty()) {
return;
@@ -115,7 +118,7 @@ void NTMechanismGroupImpl::NTUpdate(const nt::EntryNotification& event,
[](const auto& e, std::string_view name) { return e->GetName() < name; });
bool match = it != m_objects.end() && (*it)->GetName() == name;
if (event.flags & NT_NOTIFY_NEW) {
if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
if (!match) {
it = m_objects.emplace(
it, std::make_unique<NTMechanismObjectModel>(
@@ -123,6 +126,7 @@ void NTMechanismGroupImpl::NTUpdate(const nt::EntryNotification& event,
match = true;
}
}
if (match) {
if ((*it)->NTUpdate(event, childName)) {
m_objects.erase(it);
@@ -130,43 +134,74 @@ void NTMechanismGroupImpl::NTUpdate(const nt::EntryNotification& event,
}
}
bool NTMechanismObjectModel::NTUpdate(const nt::EntryNotification& event,
void NTMechanismGroupImpl::NTUpdate(const nt::ValueNotification& event,
std::string_view name) {
if (name.empty()) {
return;
}
std::string_view childName;
std::tie(name, childName) = wpi::split(name, '/');
if (childName.empty()) {
return;
}
auto it = std::lower_bound(
m_objects.begin(), m_objects.end(), name,
[](const auto& e, std::string_view name) { return e->GetName() < name; });
if (it != m_objects.end() && (*it)->GetName() == name) {
(*it)->NTUpdate(event, childName);
}
}
bool NTMechanismObjectModel::NTUpdate(const nt::TopicNotification& event,
std::string_view childName) {
if (event.entry == m_type) {
if ((event.flags & NT_NOTIFY_DELETE) != 0) {
if (event.info.topic == m_typeTopic.GetHandle()) {
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
return true;
}
if (event.value && event.value->IsString()) {
m_typeValue = event.value->GetString();
}
} else if (event.entry == m_color) {
if (event.value && event.value->IsString()) {
ConvertColor(event.value->GetString(), &m_colorValue);
}
} else if (event.entry == m_weight) {
if (event.value && event.value->IsDouble()) {
m_weightValue = event.value->GetDouble();
}
} else if (event.entry == m_angle) {
if (event.value && event.value->IsDouble()) {
m_angleValue = units::degree_t{event.value->GetDouble()};
}
} else if (event.entry == m_length) {
if (event.value && event.value->IsDouble()) {
m_lengthValue = units::meter_t{event.value->GetDouble()};
}
} else {
} else if (event.info.topic != m_colorTopic.GetHandle() &&
event.info.topic != m_weightTopic.GetHandle() &&
event.info.topic != m_angleTopic.GetHandle() &&
event.info.topic != m_lengthTopic.GetHandle()) {
m_group.NTUpdate(event, childName);
}
return false;
}
void NTMechanismObjectModel::NTUpdate(const nt::ValueNotification& event,
std::string_view childName) {
if (event.topic == m_typeTopic.GetHandle()) {
if (event.value && event.value.IsString()) {
m_typeValue = event.value.GetString();
}
} else if (event.topic == m_colorTopic.GetHandle()) {
if (event.value && event.value.IsString()) {
ConvertColor(event.value.GetString(), &m_colorValue);
}
} else if (event.topic == m_weightTopic.GetHandle()) {
if (event.value && event.value.IsDouble()) {
m_weightValue = event.value.GetDouble();
}
} else if (event.topic == m_angleTopic.GetHandle()) {
if (event.value && event.value.IsDouble()) {
m_angleValue = units::degree_t{event.value.GetDouble()};
}
} else if (event.topic == m_lengthTopic.GetHandle()) {
if (event.value && event.value.IsDouble()) {
m_lengthValue = units::meter_t{event.value.GetDouble()};
}
} else {
m_group.NTUpdate(event, childName);
}
}
class NTMechanism2DModel::RootModel final : public MechanismRootModel {
public:
RootModel(NT_Inst inst, std::string_view path, std::string_view name)
RootModel(nt::NetworkTableInstance inst, std::string_view path,
std::string_view name)
: m_group{inst, path, name},
m_x{nt::GetEntry(inst, fmt::format("{}/x", path))},
m_y{nt::GetEntry(inst, fmt::format("{}/y", path))} {}
m_xTopic{inst.GetTopic(fmt::format("{}/x", path))},
m_yTopic{inst.GetTopic(fmt::format("{}/y", path))} {}
const char* GetName() const final { return m_group.GetName(); }
void ForEachObject(
@@ -174,31 +209,24 @@ class NTMechanism2DModel::RootModel final : public MechanismRootModel {
m_group.ForEachObject(func);
}
bool NTUpdate(const nt::EntryNotification& event, std::string_view childName);
bool NTUpdate(const nt::TopicNotification& event, std::string_view childName);
void NTUpdate(const nt::ValueNotification& event, std::string_view childName);
frc::Translation2d GetPosition() const override { return m_pos; };
private:
NTMechanismGroupImpl m_group;
NT_Entry m_x;
NT_Entry m_y;
nt::Topic m_xTopic;
nt::Topic m_yTopic;
frc::Translation2d m_pos;
};
bool NTMechanism2DModel::RootModel::NTUpdate(const nt::EntryNotification& event,
bool NTMechanism2DModel::RootModel::NTUpdate(const nt::TopicNotification& event,
std::string_view childName) {
if ((event.flags & NT_NOTIFY_DELETE) != 0 &&
(event.entry == m_x || event.entry == m_y)) {
return true;
} else if (event.entry == m_x) {
if (event.value && event.value->IsDouble()) {
m_pos = frc::Translation2d{units::meter_t{event.value->GetDouble()},
m_pos.Y()};
}
} else if (event.entry == m_y) {
if (event.value && event.value->IsDouble()) {
m_pos = frc::Translation2d{m_pos.X(),
units::meter_t{event.value->GetDouble()}};
if (event.info.topic == m_xTopic.GetHandle() ||
event.info.topic == m_yTopic.GetHandle()) {
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
return true;
}
} else {
m_group.NTUpdate(event, childName);
@@ -206,36 +234,95 @@ bool NTMechanism2DModel::RootModel::NTUpdate(const nt::EntryNotification& event,
return false;
}
NTMechanism2DModel::NTMechanism2DModel(std::string_view path)
: NTMechanism2DModel{nt::GetDefaultInstance(), path} {}
void NTMechanism2DModel::RootModel::NTUpdate(const nt::ValueNotification& event,
std::string_view childName) {
if (event.topic == m_xTopic.GetHandle()) {
if (event.value && event.value.IsDouble()) {
m_pos = frc::Translation2d{units::meter_t{event.value.GetDouble()},
m_pos.Y()};
}
} else if (event.topic == m_yTopic.GetHandle()) {
if (event.value && event.value.IsDouble()) {
m_pos = frc::Translation2d{m_pos.X(),
units::meter_t{event.value.GetDouble()}};
}
} else {
m_group.NTUpdate(event, childName);
}
}
NTMechanism2DModel::NTMechanism2DModel(NT_Inst inst, std::string_view path)
: m_nt{inst},
NTMechanism2DModel::NTMechanism2DModel(std::string_view path)
: NTMechanism2DModel{nt::NetworkTableInstance::GetDefault(), path} {}
NTMechanism2DModel::NTMechanism2DModel(nt::NetworkTableInstance inst,
std::string_view path)
: m_inst{inst},
m_path{fmt::format("{}/", path)},
m_name{m_nt.GetEntry(fmt::format("{}/.name", path))},
m_dimensions{m_nt.GetEntry(fmt::format("{}/dims", path))},
m_bgColor{m_nt.GetEntry(fmt::format("{}/backgroundColor", path))},
m_tableSub{inst,
{{m_path}},
{{nt::PubSubOption::SendAll(true),
nt::PubSubOption::Periodic(0.05)}}},
m_nameTopic{m_inst.GetTopic(fmt::format("{}/.name", path))},
m_dimensionsTopic{m_inst.GetTopic(fmt::format("{}/dims", path))},
m_bgColorTopic{m_inst.GetTopic(fmt::format("{}/backgroundColor", path))},
m_topicListener{m_inst},
m_valueListener{m_inst},
m_dimensionsValue{1_m, 1_m} {
m_nt.AddListener(m_path, NT_NOTIFY_LOCAL | NT_NOTIFY_NEW | NT_NOTIFY_DELETE |
NT_NOTIFY_UPDATE | NT_NOTIFY_IMMEDIATE);
m_topicListener.Add(m_tableSub, NT_TOPIC_NOTIFY_PUBLISH |
NT_TOPIC_NOTIFY_UNPUBLISH |
NT_TOPIC_NOTIFY_IMMEDIATE);
m_valueListener.Add(m_tableSub,
NT_VALUE_NOTIFY_IMMEDIATE | NT_VALUE_NOTIFY_LOCAL);
}
NTMechanism2DModel::~NTMechanism2DModel() = default;
void NTMechanism2DModel::Update() {
for (auto&& event : m_nt.PollListener()) {
for (auto&& event : m_topicListener.ReadQueue()) {
auto name = wpi::drop_front(event.info.name, m_path.size());
if (name.empty() || name[0] == '.') {
continue;
}
std::string_view childName;
std::tie(name, childName) = wpi::split(name, '/');
if (childName.empty()) {
continue;
}
auto it = std::lower_bound(m_roots.begin(), m_roots.end(), name,
[](const auto& e, std::string_view name) {
return e->GetName() < name;
});
bool match = it != m_roots.end() && (*it)->GetName() == name;
if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
if (!match) {
it = m_roots.emplace(
it, std::make_unique<RootModel>(
m_inst, fmt::format("{}{}", m_path, name), name));
match = true;
}
}
if (match) {
if ((*it)->NTUpdate(event, childName)) {
m_roots.erase(it);
}
}
}
for (auto&& event : m_valueListener.ReadQueue()) {
// .name
if (event.entry == m_name) {
if (event.value && event.value->IsString()) {
m_nameValue = event.value->GetString();
if (event.topic == m_nameTopic.GetHandle()) {
if (event.value && event.value.IsString()) {
m_nameValue = event.value.GetString();
}
continue;
}
// dims
if (event.entry == m_dimensions) {
if (event.value && event.value->IsDoubleArray()) {
auto arr = event.value->GetDoubleArray();
if (event.topic == m_dimensionsTopic.GetHandle()) {
if (event.value && event.value.IsDoubleArray()) {
auto arr = event.value.GetDoubleArray();
if (arr.size() == 2) {
m_dimensionsValue = frc::Translation2d{units::meter_t{arr[0]},
units::meter_t{arr[1]}};
@@ -244,50 +331,16 @@ void NTMechanism2DModel::Update() {
}
// backgroundColor
if (event.entry == m_bgColor) {
if (event.value && event.value->IsString()) {
ConvertColor(event.value->GetString(), &m_bgColorValue);
}
}
std::string_view name = event.name;
if (wpi::starts_with(name, m_path)) {
name.remove_prefix(m_path.size());
if (name.empty() || name[0] == '.') {
continue;
}
std::string_view childName;
std::tie(name, childName) = wpi::split(name, '/');
if (childName.empty()) {
continue;
}
auto it = std::lower_bound(m_roots.begin(), m_roots.end(), name,
[](const auto& e, std::string_view name) {
return e->GetName() < name;
});
bool match = it != m_roots.end() && (*it)->GetName() == name;
if (event.flags & NT_NOTIFY_NEW) {
if (!match) {
it = m_roots.emplace(
it,
std::make_unique<RootModel>(
m_nt.GetInstance(), fmt::format("{}{}", m_path, name), name));
match = true;
}
}
if (match) {
if ((*it)->NTUpdate(event, childName)) {
m_roots.erase(it);
}
if (event.topic == m_bgColorTopic.GetHandle()) {
if (event.value && event.value.IsString()) {
ConvertColor(event.value.GetString(), &m_bgColorValue);
}
}
}
}
bool NTMechanism2DModel::Exists() {
return m_nt.IsConnected() && nt::GetEntryType(m_name) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_nameTopic.Exists();
}
bool NTMechanism2DModel::IsReadOnly() {

View File

@@ -10,76 +10,65 @@
using namespace glass;
NTPIDControllerModel::NTPIDControllerModel(std::string_view path)
: NTPIDControllerModel(nt::GetDefaultInstance(), path) {}
: NTPIDControllerModel(nt::NetworkTableInstance::GetDefault(), path) {}
NTPIDControllerModel::NTPIDControllerModel(NT_Inst instance,
NTPIDControllerModel::NTPIDControllerModel(nt::NetworkTableInstance inst,
std::string_view path)
: m_nt(instance),
m_name(m_nt.GetEntry(fmt::format("{}/.name", path))),
m_controllable(m_nt.GetEntry(fmt::format("{}/.controllable", path))),
m_p(m_nt.GetEntry(fmt::format("{}/p", path))),
m_i(m_nt.GetEntry(fmt::format("{}/i", path))),
m_d(m_nt.GetEntry(fmt::format("{}/d", path))),
m_setpoint(m_nt.GetEntry(fmt::format("{}/setpoint", path))),
m_pData(fmt::format("NTPIDCtrlP:{}", path)),
m_iData(fmt::format("NTPIDCtrlI:{}", path)),
m_dData(fmt::format("NTPIDCtrlD:{}", path)),
m_setpointData(fmt::format("NTPIDCtrlStpt:{}", path)),
m_nameValue(wpi::rsplit(path, '/').second) {
m_nt.AddListener(m_name);
m_nt.AddListener(m_controllable);
m_nt.AddListener(m_p);
m_nt.AddListener(m_i);
m_nt.AddListener(m_d);
m_nt.AddListener(m_setpoint);
}
: m_inst{inst},
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
m_controllable{inst.GetBooleanTopic(fmt::format("{}/.controllable", path))
.Subscribe(false)},
m_p{inst.GetDoubleTopic(fmt::format("{}/p", path))
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
m_i{inst.GetDoubleTopic(fmt::format("{}/i", path))
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
m_d{inst.GetDoubleTopic(fmt::format("{}/d", path))
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
m_setpoint{inst.GetDoubleTopic(fmt::format("{}/setpoint", path))
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
m_pData{fmt::format("NTPIDCtrlP:{}", path)},
m_iData{fmt::format("NTPIDCtrlI:{}", path)},
m_dData{fmt::format("NTPIDCtrlD:{}", path)},
m_setpointData{fmt::format("NTPIDCtrlStpt:{}", path)},
m_nameValue{wpi::rsplit(path, '/').second} {}
void NTPIDControllerModel::SetP(double value) {
nt::SetEntryValue(m_p, nt::NetworkTableValue::MakeDouble(value));
m_p.Set(value);
}
void NTPIDControllerModel::SetI(double value) {
nt::SetEntryValue(m_i, nt::NetworkTableValue::MakeDouble(value));
m_i.Set(value);
}
void NTPIDControllerModel::SetD(double value) {
nt::SetEntryValue(m_d, nt::NetworkTableValue::MakeDouble(value));
m_d.Set(value);
}
void NTPIDControllerModel::SetSetpoint(double value) {
nt::SetEntryValue(m_setpoint, nt::NetworkTableValue::MakeDouble(value));
m_setpoint.Set(value);
}
void NTPIDControllerModel::Update() {
for (auto&& event : m_nt.PollListener()) {
if (event.entry == m_name) {
if (event.value && event.value->IsString()) {
m_nameValue = event.value->GetString();
}
} else if (event.entry == m_p) {
if (event.value && event.value->IsDouble()) {
m_pData.SetValue(event.value->GetDouble());
}
} else if (event.entry == m_i) {
if (event.value && event.value->IsDouble()) {
m_iData.SetValue(event.value->GetDouble());
}
} else if (event.entry == m_d) {
if (event.value && event.value->IsDouble()) {
m_dData.SetValue(event.value->GetDouble());
}
} else if (event.entry == m_setpoint) {
if (event.value && event.value->IsDouble()) {
m_setpointData.SetValue(event.value->GetDouble());
}
} else if (event.entry == m_controllable) {
if (event.value && event.value->IsBoolean()) {
m_controllableValue = event.value->GetBoolean();
}
}
for (auto&& v : m_name.ReadQueue()) {
m_nameValue = std::move(v.value);
}
for (auto&& v : m_p.ReadQueue()) {
m_pData.SetValue(v.value, v.time);
}
for (auto&& v : m_i.ReadQueue()) {
m_iData.SetValue(v.value, v.time);
}
for (auto&& v : m_d.ReadQueue()) {
m_dData.SetValue(v.value, v.time);
}
for (auto&& v : m_setpoint.ReadQueue()) {
m_setpointData.SetValue(v.value, v.time);
}
for (auto&& v : m_controllable.ReadQueue()) {
m_controllableValue = v.value;
}
}
bool NTPIDControllerModel::Exists() {
return m_nt.IsConnected() && nt::GetEntryType(m_setpoint) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_setpoint.Exists();
}

View File

@@ -10,43 +10,35 @@
using namespace glass;
NTSpeedControllerModel::NTSpeedControllerModel(std::string_view path)
: NTSpeedControllerModel(nt::GetDefaultInstance(), path) {}
: NTSpeedControllerModel(nt::NetworkTableInstance::GetDefault(), path) {}
NTSpeedControllerModel::NTSpeedControllerModel(NT_Inst instance,
NTSpeedControllerModel::NTSpeedControllerModel(nt::NetworkTableInstance inst,
std::string_view path)
: m_nt(instance),
m_value(m_nt.GetEntry(fmt::format("{}/Value", path))),
m_name(m_nt.GetEntry(fmt::format("{}/.name", path))),
m_controllable(m_nt.GetEntry(fmt::format("{}/.controllable", path))),
m_valueData(fmt::format("NT_SpdCtrl:{}", path)),
m_nameValue(wpi::rsplit(path, '/').second) {
m_nt.AddListener(m_value);
m_nt.AddListener(m_name);
m_nt.AddListener(m_controllable);
}
: m_inst{inst},
m_value{inst.GetDoubleTopic(fmt::format("{}/Value", path))
.GetEntry(0, {{nt::PubSubOption::SendAll(true)}})},
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
m_controllable{inst.GetBooleanTopic(fmt::format("{}/.controllable", path))
.Subscribe(false)},
m_valueData{fmt::format("NT_SpdCtrl:{}", path)},
m_nameValue{wpi::rsplit(path, '/').second} {}
void NTSpeedControllerModel::SetPercent(double value) {
nt::SetEntryValue(m_value, nt::NetworkTableValue::MakeDouble(value));
m_value.Set(value);
}
void NTSpeedControllerModel::Update() {
for (auto&& event : m_nt.PollListener()) {
if (event.entry == m_value) {
if (event.value && event.value->IsDouble()) {
m_valueData.SetValue(event.value->GetDouble());
}
} else if (event.entry == m_name) {
if (event.value && event.value->IsString()) {
m_nameValue = event.value->GetString();
}
} else if (event.entry == m_controllable) {
if (event.value && event.value->IsBoolean()) {
m_controllableValue = event.value->GetBoolean();
}
}
for (auto&& v : m_value.ReadQueue()) {
m_valueData.SetValue(v.value, v.time);
}
for (auto&& v : m_name.ReadQueue()) {
m_nameValue = std::move(v.value);
}
for (auto&& v : m_controllable.ReadQueue()) {
m_controllableValue = v.value;
}
}
bool NTSpeedControllerModel::Exists() {
return m_nt.IsConnected() && nt::GetEntryType(m_value) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_value.Exists();
}

View File

@@ -9,67 +9,56 @@
using namespace glass;
NTStringChooserModel::NTStringChooserModel(std::string_view path)
: NTStringChooserModel{nt::GetDefaultInstance(), path} {}
: NTStringChooserModel{nt::NetworkTableInstance::GetDefault(), path} {}
NTStringChooserModel::NTStringChooserModel(NT_Inst inst, std::string_view path)
: m_nt{inst},
m_default{m_nt.GetEntry(fmt::format("{}/default", path))},
m_selected{m_nt.GetEntry(fmt::format("{}/selected", path))},
m_active{m_nt.GetEntry(fmt::format("{}/active", path))},
m_options{m_nt.GetEntry(fmt::format("{}/options", path))} {
m_nt.AddListener(m_default);
m_nt.AddListener(m_selected);
m_nt.AddListener(m_active);
m_nt.AddListener(m_options);
}
void NTStringChooserModel::SetDefault(std::string_view val) {
nt::SetEntryValue(m_default, nt::Value::MakeString(val));
NTStringChooserModel::NTStringChooserModel(nt::NetworkTableInstance inst,
std::string_view path)
: m_inst{inst},
m_default{
m_inst.GetStringTopic(fmt::format("{}/default", path)).Subscribe("")},
m_selected{
m_inst.GetStringTopic(fmt::format("{}/selected", path)).GetEntry("")},
m_active{
m_inst.GetStringTopic(fmt::format("{}/active", path)).Subscribe("")},
m_options{m_inst.GetStringArrayTopic(fmt::format("{}/options", path))
.Subscribe({})} {
m_selected.GetTopic().SetRetained(true);
}
void NTStringChooserModel::SetSelected(std::string_view val) {
nt::SetEntryValue(m_selected, nt::Value::MakeString(val));
}
void NTStringChooserModel::SetActive(std::string_view val) {
nt::SetEntryValue(m_active, nt::Value::MakeString(val));
}
void NTStringChooserModel::SetOptions(wpi::span<const std::string> val) {
nt::SetEntryValue(m_options, nt::Value::MakeStringArray(val));
m_selected.Set(val);
}
void NTStringChooserModel::Update() {
for (auto&& event : m_nt.PollListener()) {
if (event.entry == m_default) {
if ((event.flags & NT_NOTIFY_DELETE) != 0) {
m_defaultValue.clear();
} else if (event.value && event.value->IsString()) {
m_defaultValue = event.value->GetString();
}
} else if (event.entry == m_selected) {
if ((event.flags & NT_NOTIFY_DELETE) != 0) {
m_selectedValue.clear();
} else if (event.value && event.value->IsString()) {
m_selectedValue = event.value->GetString();
}
} else if (event.entry == m_active) {
if ((event.flags & NT_NOTIFY_DELETE) != 0) {
m_activeValue.clear();
} else if (event.value && event.value->IsString()) {
m_activeValue = event.value->GetString();
}
} else if (event.entry == m_options) {
if ((event.flags & NT_NOTIFY_DELETE) != 0) {
m_optionsValue.clear();
} else if (event.value && event.value->IsStringArray()) {
auto arr = event.value->GetStringArray();
m_optionsValue.assign(arr.begin(), arr.end());
}
}
if (!m_default.Exists()) {
m_defaultValue.clear();
}
for (auto&& v : m_default.ReadQueue()) {
m_defaultValue = std::move(v.value);
}
if (!m_selected.Exists()) {
m_selectedValue.clear();
}
for (auto&& v : m_selected.ReadQueue()) {
m_selectedValue = std::move(v.value);
}
if (!m_active.Exists()) {
m_activeValue.clear();
}
for (auto&& v : m_active.ReadQueue()) {
m_activeValue = std::move(v.value);
}
if (!m_options.Exists()) {
m_optionsValue.clear();
}
for (auto&& v : m_options.ReadQueue()) {
m_optionsValue = std::move(v.value);
}
}
bool NTStringChooserModel::Exists() {
return m_nt.IsConnected() && nt::GetEntryType(m_options) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_options.Exists();
}

View File

@@ -9,37 +9,30 @@
using namespace glass;
NTSubsystemModel::NTSubsystemModel(std::string_view path)
: NTSubsystemModel(nt::GetDefaultInstance(), path) {}
: NTSubsystemModel(nt::NetworkTableInstance::GetDefault(), path) {}
NTSubsystemModel::NTSubsystemModel(NT_Inst instance, std::string_view path)
: m_nt(instance),
m_name(m_nt.GetEntry(fmt::format("{}/.name", path))),
m_defaultCommand(m_nt.GetEntry(fmt::format("{}/.default", path))),
m_currentCommand(m_nt.GetEntry(fmt::format("{}/.command", path))) {
m_nt.AddListener(m_name);
m_nt.AddListener(m_defaultCommand);
m_nt.AddListener(m_currentCommand);
NTSubsystemModel::NTSubsystemModel(nt::NetworkTableInstance inst,
std::string_view path)
: m_inst{inst},
m_name{inst.GetStringTopic(fmt::format("{}/.name", path)).Subscribe("")},
m_defaultCommand{
inst.GetStringTopic(fmt::format("{}/.default", path)).Subscribe("")},
m_currentCommand{
inst.GetStringTopic(fmt::format("{}/.command", path)).Subscribe("")} {
}
void NTSubsystemModel::Update() {
for (auto&& event : m_nt.PollListener()) {
if (event.entry == m_name) {
if (event.value && event.value->IsString()) {
m_nameValue = event.value->GetString();
}
} else if (event.entry == m_defaultCommand) {
if (event.value && event.value->IsString()) {
m_defaultCommandValue = event.value->GetString();
}
} else if (event.entry == m_currentCommand) {
if (event.value && event.value->IsString()) {
m_currentCommandValue = event.value->GetString();
}
}
for (auto&& v : m_name.ReadQueue()) {
m_nameValue = std::move(v.value);
}
for (auto&& v : m_defaultCommand.ReadQueue()) {
m_defaultCommandValue = std::move(v.value);
}
for (auto&& v : m_currentCommand.ReadQueue()) {
m_currentCommandValue = std::move(v.value);
}
}
bool NTSubsystemModel::Exists() {
return m_nt.IsConnected() &&
nt::GetEntryType(m_defaultCommand) != NT_UNASSIGNED;
return m_inst.IsConnected() && m_defaultCommand.Exists();
}

File diff suppressed because it is too large Load Diff

View File

@@ -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.
#include "glass/networktables/NetworkTablesHelper.h"
using namespace glass;
NetworkTablesHelper::NetworkTablesHelper(NT_Inst inst)
: m_inst{inst}, m_poller{nt::CreateEntryListenerPoller(inst)} {}
NetworkTablesHelper::~NetworkTablesHelper() {
nt::DestroyEntryListenerPoller(m_poller);
}
bool NetworkTablesHelper::IsConnected() const {
return nt::GetNetworkMode(m_inst) == NT_NET_MODE_SERVER ||
nt::IsConnected(m_inst);
}

View File

@@ -17,16 +17,19 @@
using namespace glass;
NetworkTablesProvider::NetworkTablesProvider(Storage& storage)
: NetworkTablesProvider{storage, nt::GetDefaultInstance()} {}
: NetworkTablesProvider{storage, nt::NetworkTableInstance::GetDefault()} {}
NetworkTablesProvider::NetworkTablesProvider(Storage& storage, NT_Inst inst)
NetworkTablesProvider::NetworkTablesProvider(Storage& storage,
nt::NetworkTableInstance inst)
: Provider{storage.GetChild("windows")},
m_nt{inst},
m_inst{inst},
m_topicPoller{inst},
m_valuePoller{inst},
m_typeCache{storage.GetChild("types")} {
storage.SetCustomApply([this] {
m_listener =
m_nt.AddListener("", NT_NOTIFY_LOCAL | NT_NOTIFY_NEW |
NT_NOTIFY_DELETE | NT_NOTIFY_IMMEDIATE);
m_topicListener = m_topicPoller.Add({{""}}, NT_TOPIC_NOTIFY_PUBLISH |
NT_TOPIC_NOTIFY_UNPUBLISH |
NT_TOPIC_NOTIFY_IMMEDIATE);
for (auto&& childIt : m_storage.GetChildren()) {
auto id = childIt.key();
auto typePtr = m_typeCache.FindValue(id);
@@ -41,16 +44,15 @@ NetworkTablesProvider::NetworkTablesProvider(Storage& storage, NT_Inst inst)
}
auto entry = GetOrCreateView(
builderIt->second,
nt::GetEntry(m_nt.GetInstance(), fmt::format("{}/.type", id)), id);
builderIt->second, m_inst.GetTopic(fmt::format("{}/.type", id)), id);
if (entry) {
Show(entry, nullptr);
}
}
});
storage.SetCustomClear([this, &storage] {
nt::RemoveEntryListener(m_listener);
m_listener = 0;
m_topicPoller.Remove(m_topicListener);
m_topicListener = 0;
for (auto&& modelEntry : m_modelEntries) {
modelEntry->model.reset();
}
@@ -101,35 +103,57 @@ void NetworkTablesProvider::Update() {
Provider::Update();
// add/remove entries from NT changes
for (auto&& event : m_nt.PollListener()) {
for (auto&& event : m_topicPoller.ReadQueue()) {
// look for .type fields
std::string_view eventName{event.name};
if (!wpi::ends_with(eventName, "/.type") || !event.value ||
!event.value->IsString()) {
if (!wpi::ends_with(event.info.name, "/.type") ||
event.info.type != NT_STRING || event.info.type_str != "string") {
continue;
}
if (event.flags & NT_TOPIC_NOTIFY_UNPUBLISH) {
auto it = m_topicMap.find(event.info.topic);
if (it != m_topicMap.end()) {
m_valuePoller.Remove(it->second.listener);
m_topicMap.erase(it);
}
auto it2 = std::find_if(
m_viewEntries.begin(), m_viewEntries.end(), [&](const auto& elem) {
return static_cast<Entry*>(elem->modelEntry)
->typeTopic.GetHandle() == event.info.topic;
});
if (it2 != m_viewEntries.end()) {
m_viewEntries.erase(it2);
}
} else if (event.flags & NT_TOPIC_NOTIFY_PUBLISH) {
// subscribe to it
SubListener sublistener;
sublistener.subscriber = nt::StringTopic{event.info.topic}.Subscribe("");
sublistener.listener =
m_valuePoller.Add(sublistener.subscriber,
NT_VALUE_NOTIFY_LOCAL | NT_VALUE_NOTIFY_IMMEDIATE);
m_topicMap.try_emplace(event.info.topic, std::move(sublistener));
}
}
// handle actual .type strings
for (auto&& event : m_valuePoller.ReadQueue()) {
if (!event.value.IsString()) {
continue;
}
auto tableName = wpi::drop_back(eventName, 6);
// only handle ones where we have a builder
auto builderIt = m_typeMap.find(event.value->GetString());
auto builderIt = m_typeMap.find(event.value.GetString());
if (builderIt == m_typeMap.end()) {
continue;
}
if (event.flags & NT_NOTIFY_DELETE) {
auto it = std::find_if(
m_viewEntries.begin(), m_viewEntries.end(), [&](const auto& elem) {
return static_cast<Entry*>(elem->modelEntry)->typeEntry ==
event.entry;
});
if (it != m_viewEntries.end()) {
m_viewEntries.erase(it);
}
} else if (event.flags & NT_NOTIFY_NEW) {
GetOrCreateView(builderIt->second, event.entry, tableName);
// cache the type
m_typeCache.SetString(tableName, event.value->GetString());
}
auto topicName = nt::GetTopicName(event.topic);
auto tableName = wpi::drop_back(topicName, 6);
GetOrCreateView(builderIt->second, nt::Topic{event.topic}, tableName);
// cache the type
m_typeCache.SetString(tableName, event.value.GetString());
}
}
@@ -149,7 +173,7 @@ void NetworkTablesProvider::Show(ViewEntry* entry, Window* window) {
// get or create model
if (!entry->modelEntry->model) {
entry->modelEntry->model =
entry->modelEntry->createModel(m_nt.GetInstance(), entry->name.c_str());
entry->modelEntry->createModel(m_inst, entry->name.c_str());
}
if (!entry->modelEntry->model) {
return;
@@ -180,22 +204,22 @@ void NetworkTablesProvider::Show(ViewEntry* entry, Window* window) {
}
NetworkTablesProvider::ViewEntry* NetworkTablesProvider::GetOrCreateView(
const Builder& builder, NT_Entry typeEntry, std::string_view name) {
const Builder& builder, nt::Topic typeTopic, std::string_view name) {
// get view entry if it already exists
auto viewIt = FindViewEntry(name);
if (viewIt != m_viewEntries.end() && (*viewIt)->name == name) {
// make sure typeEntry is set in model
static_cast<Entry*>((*viewIt)->modelEntry)->typeEntry = typeEntry;
static_cast<Entry*>((*viewIt)->modelEntry)->typeTopic = typeTopic;
return viewIt->get();
}
// get or create model entry
auto modelIt = FindModelEntry(name);
if (modelIt != m_modelEntries.end() && (*modelIt)->name == name) {
static_cast<Entry*>(modelIt->get())->typeEntry = typeEntry;
static_cast<Entry*>(modelIt->get())->typeTopic = typeTopic;
} else {
modelIt = m_modelEntries.emplace(
modelIt, std::make_unique<Entry>(typeEntry, name, builder));
modelIt, std::make_unique<Entry>(typeTopic, name, builder));
}
// create new view entry

View File

@@ -43,50 +43,63 @@ void NetworkTablesSettings::Thread::Main() {
// if just changing servers in client mode, no need to stop and restart
unsigned int curMode = nt::GetNetworkMode(m_inst);
if (mode != 1 || (curMode & NT_NET_MODE_SERVER) != 0) {
if ((mode == 0 || mode == 3) ||
(mode == 1 && (curMode & NT_NET_MODE_CLIENT4) == 0) ||
(mode == 2 && (curMode & NT_NET_MODE_CLIENT3) == 0)) {
nt::StopClient(m_inst);
nt::StopServer(m_inst);
nt::StopLocal(m_inst);
}
if (m_mode != 1 || !dsClient) {
if ((m_mode == 0 || m_mode == 3) || !dsClient) {
nt::StopDSClient(m_inst);
}
lock.lock();
} while (mode != m_mode || dsClient != m_dsClient);
if (m_mode == 1) {
if (m_mode == 1 || m_mode == 2) {
std::string_view serverTeam{m_serverTeam};
std::optional<unsigned int> team;
nt::SetNetworkIdentity(m_inst, m_clientName);
if (m_mode == 1) {
nt::StartClient4(m_inst);
} else if (m_mode == 2) {
nt::StartClient3(m_inst);
}
if (!wpi::contains(serverTeam, '.') &&
(team = wpi::parse_integer<unsigned int>(serverTeam, 10))) {
nt::StartClientTeam(m_inst, team.value(), NT_DEFAULT_PORT);
nt::SetServerTeam(m_inst, team.value(), 0);
} else {
wpi::SmallVector<std::string_view, 4> serverNames;
wpi::SmallVector<std::pair<std::string_view, unsigned int>, 4> servers;
std::vector<std::pair<std::string_view, unsigned int>> servers;
wpi::split(serverTeam, serverNames, ',', -1, false);
for (auto&& serverName : serverNames) {
servers.emplace_back(serverName, NT_DEFAULT_PORT);
servers.emplace_back(serverName, 0);
}
nt::StartClient(m_inst, servers);
nt::SetServer(m_inst, servers);
}
if (m_dsClient) {
nt::StartDSClient(m_inst, NT_DEFAULT_PORT);
nt::StartDSClient(m_inst, 0);
}
} else if (m_mode == 2) {
} else if (m_mode == 3) {
nt::StartServer(m_inst, m_iniName.c_str(), m_listenAddress.c_str(),
NT_DEFAULT_PORT);
NT_DEFAULT_PORT3, NT_DEFAULT_PORT4);
}
}
}
NetworkTablesSettings::NetworkTablesSettings(Storage& storage, NT_Inst inst)
: m_mode{storage.GetString("mode"), 0, {"Disabled", "Client", "Server"}},
m_iniName{storage.GetString("iniName", "networktables.ini")},
NetworkTablesSettings::NetworkTablesSettings(std::string_view clientName,
Storage& storage, NT_Inst inst)
: m_mode{storage.GetString("mode"),
0,
{"Disabled", "Client (NT4)", "Client (NT3)", "Server"}},
m_persistentFilename{
storage.GetString("persistentFilename", "networktables.json")},
m_serverTeam{storage.GetString("serverTeam")},
m_listenAddress{storage.GetString("listenAddress")},
m_clientName{storage.GetString("clientName", clientName)},
m_dsClient{storage.GetBool("dsClient", true)} {
m_thread.Start(inst);
}
@@ -101,23 +114,26 @@ void NetworkTablesSettings::Update() {
auto thr = m_thread.GetThread();
thr->m_restart = true;
thr->m_mode = m_mode.GetValue();
thr->m_iniName = m_iniName;
thr->m_iniName = m_persistentFilename;
thr->m_serverTeam = m_serverTeam;
thr->m_listenAddress = m_listenAddress;
thr->m_clientName = m_clientName;
thr->m_dsClient = m_dsClient;
thr->m_cond.notify_one();
}
bool NetworkTablesSettings::Display() {
m_mode.Combo("Mode", m_serverOption ? 3 : 2);
m_mode.Combo("Mode", m_serverOption ? 4 : 3);
switch (m_mode.GetValue()) {
case 1:
case 2:
ImGui::InputText("Team/IP", &m_serverTeam);
ImGui::InputText("Network Identity", &m_clientName);
ImGui::Checkbox("Get Address from DS", &m_dsClient);
break;
case 2:
case 3:
ImGui::InputText("Listen Address", &m_listenAddress);
ImGui::InputText("ini Filename", &m_iniName);
ImGui::InputText("Persistent Filename", &m_persistentFilename);
break;
default:
break;

View File

@@ -23,7 +23,7 @@ using namespace glass;
void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
provider.Register(
NTCommandSchedulerModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTCommandSchedulerModel>(inst, path);
},
[](Window* win, Model* model, const char*) {
@@ -34,7 +34,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
});
provider.Register(
NTCommandSelectorModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTCommandSelectorModel>(inst, path);
},
[](Window* win, Model* model, const char*) {
@@ -45,7 +45,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
});
provider.Register(
NTDifferentialDriveModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTDifferentialDriveModel>(inst, path);
},
[](Window* win, Model* model, const char*) {
@@ -56,7 +56,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
});
provider.Register(
NTFMSModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTFMSModel>(inst, path);
},
[](Window* win, Model* model, const char*) {
@@ -66,7 +66,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
});
provider.Register(
NTDigitalInputModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTDigitalInputModel>(inst, path);
},
[](Window* win, Model* model, const char*) {
@@ -77,7 +77,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
});
provider.Register(
NTDigitalOutputModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTDigitalOutputModel>(inst, path);
},
[](Window* win, Model* model, const char*) {
@@ -88,7 +88,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
});
provider.Register(
NTField2DModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTField2DModel>(inst, path);
},
[=](Window* win, Model* model, const char* path) {
@@ -100,7 +100,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
});
provider.Register(
NTGyroModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTGyroModel>(inst, path);
},
[](Window* win, Model* model, const char* path) {
@@ -110,7 +110,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
});
provider.Register(
NTMecanumDriveModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTMecanumDriveModel>(inst, path);
},
[](Window* win, Model* model, const char*) {
@@ -120,7 +120,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
});
provider.Register(
NTMechanism2DModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTMechanism2DModel>(inst, path);
},
[=](Window* win, Model* model, const char* path) {
@@ -132,7 +132,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
});
provider.Register(
NTPIDControllerModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTPIDControllerModel>(inst, path);
},
[](Window* win, Model* model, const char* path) {
@@ -143,7 +143,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
});
provider.Register(
NTSpeedControllerModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTSpeedControllerModel>(inst, path);
},
[](Window* win, Model* model, const char* path) {
@@ -154,7 +154,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
});
provider.Register(
NTStringChooserModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTStringChooserModel>(inst, path);
},
[](Window* win, Model* model, const char*) {
@@ -165,7 +165,7 @@ void glass::AddStandardNetworkTablesViews(NetworkTablesProvider& provider) {
});
provider.Register(
NTSubsystemModel::kType,
[](NT_Inst inst, const char* path) {
[](nt::NetworkTableInstance inst, const char* path) {
return std::make_unique<NTSubsystemModel>(inst, path);
},
[](Window* win, Model* model, const char*) {