blob: 745e9e2b54cd45047bb11e2f215ad60c9c6443be [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#include "glass/networktables/NTField2D.h"
6
7#include <algorithm>
8#include <vector>
9
10#include <fmt/format.h>
James Kuszmaulcf324122023-01-14 14:07:17 -080011#include <networktables/DoubleArrayTopic.h>
12#include <networktables/MultiSubscriber.h>
Austin Schuh812d0d12021-11-04 20:16:48 -070013#include <ntcore_cpp.h>
14#include <wpi/Endian.h>
15#include <wpi/MathExtras.h>
16#include <wpi/SmallVector.h>
17#include <wpi/StringExtras.h>
18
19using namespace glass;
20
21class NTField2DModel::ObjectModel : public FieldObjectModel {
22 public:
James Kuszmaulcf324122023-01-14 14:07:17 -080023 ObjectModel(std::string_view name, nt::DoubleArrayTopic topic)
24 : m_name{name}, m_topic{topic} {}
Austin Schuh812d0d12021-11-04 20:16:48 -070025
26 const char* GetName() const override { return m_name.c_str(); }
James Kuszmaulcf324122023-01-14 14:07:17 -080027 nt::DoubleArrayTopic GetTopic() const { return m_topic; }
Austin Schuh812d0d12021-11-04 20:16:48 -070028
29 void NTUpdate(const nt::Value& value);
30
James Kuszmaulcf324122023-01-14 14:07:17 -080031 void Update() override {}
32 bool Exists() override { return m_topic.Exists(); }
Austin Schuh812d0d12021-11-04 20:16:48 -070033 bool IsReadOnly() override { return false; }
34
James Kuszmaulcf324122023-01-14 14:07:17 -080035 std::span<const frc::Pose2d> GetPoses() override { return m_poses; }
36 void SetPoses(std::span<const frc::Pose2d> poses) override;
Austin Schuh812d0d12021-11-04 20:16:48 -070037 void SetPose(size_t i, frc::Pose2d pose) override;
38 void SetPosition(size_t i, frc::Translation2d pos) override;
39 void SetRotation(size_t i, frc::Rotation2d rot) override;
40
41 private:
42 void UpdateNT();
43
44 std::string m_name;
James Kuszmaulcf324122023-01-14 14:07:17 -080045 nt::DoubleArrayTopic m_topic;
46 nt::DoubleArrayPublisher m_pub;
Austin Schuh812d0d12021-11-04 20:16:48 -070047
48 std::vector<frc::Pose2d> m_poses;
49};
50
51void NTField2DModel::ObjectModel::NTUpdate(const nt::Value& value) {
52 if (value.IsDoubleArray()) {
53 auto arr = value.GetDoubleArray();
54 auto size = arr.size();
55 if ((size % 3) != 0) {
56 return;
57 }
58 m_poses.resize(size / 3);
59 for (size_t i = 0; i < size / 3; ++i) {
60 m_poses[i] = frc::Pose2d{
61 units::meter_t{arr[i * 3 + 0]}, units::meter_t{arr[i * 3 + 1]},
62 frc::Rotation2d{units::degree_t{arr[i * 3 + 2]}}};
63 }
Austin Schuh812d0d12021-11-04 20:16:48 -070064 }
65}
66
67void NTField2DModel::ObjectModel::UpdateNT() {
James Kuszmaulcf324122023-01-14 14:07:17 -080068 wpi::SmallVector<double, 9> arr;
69 for (auto&& pose : m_poses) {
70 auto& translation = pose.Translation();
71 arr.push_back(translation.X().value());
72 arr.push_back(translation.Y().value());
73 arr.push_back(pose.Rotation().Degrees().value());
Austin Schuh812d0d12021-11-04 20:16:48 -070074 }
James Kuszmaulcf324122023-01-14 14:07:17 -080075 if (!m_pub) {
76 m_pub = m_topic.Publish();
77 }
78 m_pub.Set(arr);
Austin Schuh812d0d12021-11-04 20:16:48 -070079}
80
James Kuszmaulcf324122023-01-14 14:07:17 -080081void NTField2DModel::ObjectModel::SetPoses(std::span<const frc::Pose2d> poses) {
Austin Schuh812d0d12021-11-04 20:16:48 -070082 m_poses.assign(poses.begin(), poses.end());
83 UpdateNT();
84}
85
86void NTField2DModel::ObjectModel::SetPose(size_t i, frc::Pose2d pose) {
87 if (i < m_poses.size()) {
88 m_poses[i] = pose;
89 UpdateNT();
90 }
91}
92
93void NTField2DModel::ObjectModel::SetPosition(size_t i,
94 frc::Translation2d pos) {
95 if (i < m_poses.size()) {
96 m_poses[i] = frc::Pose2d{pos, m_poses[i].Rotation()};
97 UpdateNT();
98 }
99}
100
101void NTField2DModel::ObjectModel::SetRotation(size_t i, frc::Rotation2d rot) {
102 if (i < m_poses.size()) {
103 m_poses[i] = frc::Pose2d{m_poses[i].Translation(), rot};
104 UpdateNT();
105 }
106}
107
108NTField2DModel::NTField2DModel(std::string_view path)
James Kuszmaulcf324122023-01-14 14:07:17 -0800109 : NTField2DModel{nt::NetworkTableInstance::GetDefault(), path} {}
Austin Schuh812d0d12021-11-04 20:16:48 -0700110
James Kuszmaulcf324122023-01-14 14:07:17 -0800111NTField2DModel::NTField2DModel(nt::NetworkTableInstance inst,
112 std::string_view path)
113 : m_path{fmt::format("{}/", path)},
114 m_inst{inst},
115 m_tableSub{inst, {{m_path}}, {.periodic = 0.05, .sendAll = true}},
116 m_nameTopic{inst.GetTopic(fmt::format("{}/.name", path))},
117 m_poller{inst} {
118 m_poller.AddListener(m_tableSub, nt::EventFlags::kTopic |
119 nt::EventFlags::kValueAll |
120 nt::EventFlags::kImmediate);
Austin Schuh812d0d12021-11-04 20:16:48 -0700121}
122
123NTField2DModel::~NTField2DModel() = default;
124
125void NTField2DModel::Update() {
James Kuszmaulcf324122023-01-14 14:07:17 -0800126 for (auto&& event : m_poller.ReadQueue()) {
127 if (auto info = event.GetTopicInfo()) {
128 // handle publish/unpublish
129 auto name = wpi::drop_front(info->name, m_path.size());
Austin Schuh812d0d12021-11-04 20:16:48 -0700130 if (name.empty() || name[0] == '.') {
131 continue;
132 }
James Kuszmaulcf324122023-01-14 14:07:17 -0800133 auto [it, match] = Find(info->name);
134 if (event.flags & nt::EventFlags::kUnpublish) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700135 if (match) {
136 m_objects.erase(it);
137 }
138 continue;
James Kuszmaulcf324122023-01-14 14:07:17 -0800139 } else if (event.flags & nt::EventFlags::kPublish) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700140 if (!match) {
141 it = m_objects.emplace(
James Kuszmaulcf324122023-01-14 14:07:17 -0800142 it, std::make_unique<ObjectModel>(
143 info->name, nt::DoubleArrayTopic{info->topic}));
Austin Schuh812d0d12021-11-04 20:16:48 -0700144 }
145 } else if (!match) {
146 continue;
147 }
James Kuszmaulcf324122023-01-14 14:07:17 -0800148 } else if (auto valueData = event.GetValueEventData()) {
149 // update values
150 // .name
151 if (valueData->topic == m_nameTopic.GetHandle()) {
152 if (valueData->value && valueData->value.IsString()) {
153 m_nameValue = valueData->value.GetString();
154 }
155 continue;
156 }
157
158 auto it =
159 std::find_if(m_objects.begin(), m_objects.end(), [&](const auto& e) {
160 return e->GetTopic().GetHandle() == valueData->topic;
161 });
162 if (it != m_objects.end()) {
163 (*it)->NTUpdate(valueData->value);
164 continue;
Austin Schuh812d0d12021-11-04 20:16:48 -0700165 }
166 }
167 }
168}
169
170bool NTField2DModel::Exists() {
James Kuszmaulcf324122023-01-14 14:07:17 -0800171 return m_nameTopic.Exists();
Austin Schuh812d0d12021-11-04 20:16:48 -0700172}
173
174bool NTField2DModel::IsReadOnly() {
175 return false;
176}
177
178FieldObjectModel* NTField2DModel::AddFieldObject(std::string_view name) {
179 auto fullName = fmt::format("{}{}", m_path, name);
180 auto [it, match] = Find(fullName);
181 if (!match) {
James Kuszmaulcf324122023-01-14 14:07:17 -0800182 it = m_objects.emplace(it,
183 std::make_unique<ObjectModel>(
184 fullName, m_inst.GetDoubleArrayTopic(fullName)));
Austin Schuh812d0d12021-11-04 20:16:48 -0700185 }
186 return it->get();
187}
188
189void NTField2DModel::RemoveFieldObject(std::string_view name) {
190 auto [it, match] = Find(fmt::format("{}{}", m_path, name));
191 if (match) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700192 m_objects.erase(it);
193 }
194}
195
196void NTField2DModel::ForEachFieldObject(
197 wpi::function_ref<void(FieldObjectModel& model, std::string_view name)>
198 func) {
199 for (auto&& obj : m_objects) {
200 if (obj->Exists()) {
201 func(*obj, wpi::drop_front(obj->GetName(), m_path.size()));
202 }
203 }
204}
205
206std::pair<NTField2DModel::Objects::iterator, bool> NTField2DModel::Find(
207 std::string_view fullName) {
208 auto it = std::lower_bound(
209 m_objects.begin(), m_objects.end(), fullName,
210 [](const auto& e, std::string_view name) { return e->GetName() < name; });
211 return {it, it != m_objects.end() && (*it)->GetName() == fullName};
212}