mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-30 02:31:44 +00:00
[ntcore] NetworkTables 4 (#3217)
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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*) {
|
||||
|
||||
Reference in New Issue
Block a user