blob: fcb0f1599c413edbc9404b0fa5b116428a64350e [file] [log] [blame]
Parker Schuh18dbbb42017-10-18 21:45:33 -07001#include "frc971/codelab/basic.h"
2
3#include <unistd.h>
4
5#include <chrono>
6#include <memory>
7
John Park33858a32018-09-28 23:05:48 -07008#include "aos/controls/control_loop_test.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07009#include "aos/events/shm_event_loop.h"
10#include "frc971/codelab/basic_generated.h"
Parker Schuh18dbbb42017-10-18 21:45:33 -070011#include "frc971/control_loops/team_number_test_environment.h"
12#include "gtest/gtest.h"
13
14namespace frc971 {
15namespace codelab {
16namespace testing {
17
Austin Schuh9fe68f72019-08-10 19:32:03 -070018namespace chrono = ::std::chrono;
19using aos::monotonic_clock;
20
Parker Schuh18dbbb42017-10-18 21:45:33 -070021// Class which simulates stuff and sends out queue messages with the
22// position.
23class BasicSimulation {
24 public:
Austin Schuh9fe68f72019-08-10 19:32:03 -070025 BasicSimulation(::aos::EventLoop *event_loop, chrono::nanoseconds dt)
26 : event_loop_(event_loop),
Alex Perrycb7da4b2019-08-28 19:35:56 -070027 position_sender_(event_loop_->MakeSender<Position>("/codelab")),
28 status_fetcher_(event_loop_->MakeFetcher<Status>("/codelab")),
29 output_fetcher_(event_loop_->MakeFetcher<Output>("/codelab")) {
Austin Schuh9fe68f72019-08-10 19:32:03 -070030 event_loop_->AddPhasedLoop(
31 [this](int) {
32 // Skip this the first time.
33 if (!first_) {
34 Simulate();
35 }
36 first_ = false;
37 SendPositionMessage();
38 },
39 dt);
40 }
Parker Schuh18dbbb42017-10-18 21:45:33 -070041
42 // Sends a queue message with the position data.
43 void SendPositionMessage() {
Alex Perrycb7da4b2019-08-28 19:35:56 -070044 auto builder = position_sender_.MakeBuilder();
Parker Schuh18dbbb42017-10-18 21:45:33 -070045
Alex Perrycb7da4b2019-08-28 19:35:56 -070046 Position::Builder position_builder = builder.MakeBuilder<Position>();
Parker Schuh18dbbb42017-10-18 21:45:33 -070047
Alex Perrycb7da4b2019-08-28 19:35:56 -070048 position_builder.add_limit_sensor(limit_sensor_);
49
50 builder.Send(position_builder.Finish());
Parker Schuh18dbbb42017-10-18 21:45:33 -070051 }
52
53 void VerifyResults(double voltage, bool status) {
Austin Schuh9fe68f72019-08-10 19:32:03 -070054 output_fetcher_.Fetch();
55 status_fetcher_.Fetch();
Parker Schuh18dbbb42017-10-18 21:45:33 -070056
Austin Schuh9fe68f72019-08-10 19:32:03 -070057 ASSERT_TRUE(output_fetcher_.get() != nullptr);
58 ASSERT_TRUE(status_fetcher_.get() != nullptr);
Parker Schuh18dbbb42017-10-18 21:45:33 -070059
Alex Perrycb7da4b2019-08-28 19:35:56 -070060 EXPECT_EQ(output_fetcher_->intake_voltage(), voltage);
61 EXPECT_EQ(status_fetcher_->intake_complete(), status);
Parker Schuh18dbbb42017-10-18 21:45:33 -070062 }
63
64 void set_limit_sensor(bool value) { limit_sensor_ = value; }
65
66 // Simulates basic control loop for a single timestep.
Austin Schuh9fe68f72019-08-10 19:32:03 -070067 void Simulate() { EXPECT_TRUE(output_fetcher_.Fetch()); }
Parker Schuh18dbbb42017-10-18 21:45:33 -070068
69 private:
Austin Schuh9fe68f72019-08-10 19:32:03 -070070 ::aos::EventLoop *event_loop_;
71
Alex Perrycb7da4b2019-08-28 19:35:56 -070072 ::aos::Sender<Position> position_sender_;
73 ::aos::Fetcher<Status> status_fetcher_;
74 ::aos::Fetcher<Output> output_fetcher_;
Austin Schuh9fe68f72019-08-10 19:32:03 -070075
Parker Schuh18dbbb42017-10-18 21:45:33 -070076 bool limit_sensor_ = false;
Austin Schuh9fe68f72019-08-10 19:32:03 -070077
78 bool first_ = true;
Parker Schuh18dbbb42017-10-18 21:45:33 -070079};
80
81class BasicControlLoopTest : public ::aos::testing::ControlLoopTest {
82 public:
83 BasicControlLoopTest()
Alex Perrycb7da4b2019-08-28 19:35:56 -070084 : ::aos::testing::ControlLoopTest(
85 "{\n"
86 " \"channels\": [ \n"
87 " {\n"
88 " \"name\": \"/aos\",\n"
89 " \"type\": \"aos.JoystickState\"\n"
90 " },\n"
91 " {\n"
92 " \"name\": \"/aos\",\n"
Tyler Chatow67ddb032020-01-12 14:30:04 -080093 " \"type\": \"aos.LogMessageFbs\"\n"
94 " },\n"
95 " {\n"
96 " \"name\": \"/aos\",\n"
Alex Perrycb7da4b2019-08-28 19:35:56 -070097 " \"type\": \"aos.RobotState\"\n"
98 " },\n"
99 " {\n"
100 " \"name\": \"/codelab\",\n"
101 " \"type\": \"frc971.codelab.Goal\"\n"
102 " },\n"
103 " {\n"
104 " \"name\": \"/codelab\",\n"
105 " \"type\": \"frc971.codelab.Output\"\n"
106 " },\n"
107 " {\n"
108 " \"name\": \"/codelab\",\n"
109 " \"type\": \"frc971.codelab.Status\"\n"
110 " },\n"
111 " {\n"
112 " \"name\": \"/codelab\",\n"
113 " \"type\": \"frc971.codelab.Position\"\n"
114 " }\n"
115 " ]\n"
116 "}\n",
117 chrono::microseconds(5050)),
Austin Schuh5f1cc5c2019-12-01 18:01:11 -0800118 test_event_loop_(MakeEventLoop("test")),
Alex Perrycb7da4b2019-08-28 19:35:56 -0700119 goal_sender_(test_event_loop_->MakeSender<Goal>("/codelab")),
Austin Schuh9fe68f72019-08-10 19:32:03 -0700120
Austin Schuh5f1cc5c2019-12-01 18:01:11 -0800121 basic_event_loop_(MakeEventLoop("basic")),
Alex Perrycb7da4b2019-08-28 19:35:56 -0700122 basic_(basic_event_loop_.get(), "/codelab"),
Austin Schuh9fe68f72019-08-10 19:32:03 -0700123
Austin Schuh5f1cc5c2019-12-01 18:01:11 -0800124 basic_simulation_event_loop_(MakeEventLoop("simulation")),
Austin Schuh9fe68f72019-08-10 19:32:03 -0700125 basic_simulation_(basic_simulation_event_loop_.get(), dt()) {
Parker Schuh18dbbb42017-10-18 21:45:33 -0700126 set_team_id(control_loops::testing::kTeamNumber);
127 }
128
Austin Schuh9fe68f72019-08-10 19:32:03 -0700129 ::std::unique_ptr<::aos::EventLoop> test_event_loop_;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700130 ::aos::Sender<Goal> goal_sender_;
Parker Schuh18dbbb42017-10-18 21:45:33 -0700131
Austin Schuh9fe68f72019-08-10 19:32:03 -0700132 ::std::unique_ptr<::aos::EventLoop> basic_event_loop_;
133 Basic basic_;
Parker Schuh18dbbb42017-10-18 21:45:33 -0700134
Austin Schuh9fe68f72019-08-10 19:32:03 -0700135 ::std::unique_ptr<::aos::EventLoop> basic_simulation_event_loop_;
Parker Schuh18dbbb42017-10-18 21:45:33 -0700136 BasicSimulation basic_simulation_;
137};
138
139// Tests that when the motor has finished intaking it stops.
140TEST_F(BasicControlLoopTest, IntakeLimitTransitionsToTrue) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700141 {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700142 auto builder = goal_sender_.MakeBuilder();
143 Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
144 goal_builder.add_intake(true);
145 ASSERT_TRUE(builder.Send(goal_builder.Finish()));
Austin Schuh9fe68f72019-08-10 19:32:03 -0700146 }
147
Parker Schuh18dbbb42017-10-18 21:45:33 -0700148 basic_simulation_.set_limit_sensor(false);
149
Austin Schuh9fe68f72019-08-10 19:32:03 -0700150 RunFor(dt() * 2);
Parker Schuh18dbbb42017-10-18 21:45:33 -0700151
152 basic_simulation_.VerifyResults(12.0, false);
153
Austin Schuh9fe68f72019-08-10 19:32:03 -0700154 {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700155 auto builder = goal_sender_.MakeBuilder();
156 Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
157 goal_builder.add_intake(true);
158 ASSERT_TRUE(builder.Send(goal_builder.Finish()));
Austin Schuh9fe68f72019-08-10 19:32:03 -0700159 }
Parker Schuh18dbbb42017-10-18 21:45:33 -0700160 basic_simulation_.set_limit_sensor(true);
161
Austin Schuh9fe68f72019-08-10 19:32:03 -0700162 RunFor(dt() * 2);
Parker Schuh18dbbb42017-10-18 21:45:33 -0700163
164 basic_simulation_.VerifyResults(0.0, true);
165}
166
167// Tests that the intake goes on if the sensor is not pressed
168// and intake is requested.
169TEST_F(BasicControlLoopTest, IntakeLimitNotSet) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700170 {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700171 auto builder = goal_sender_.MakeBuilder();
172 Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
173 goal_builder.add_intake(true);
174 ASSERT_TRUE(builder.Send(goal_builder.Finish()));
Austin Schuh9fe68f72019-08-10 19:32:03 -0700175 }
Parker Schuh18dbbb42017-10-18 21:45:33 -0700176 basic_simulation_.set_limit_sensor(false);
177
Austin Schuh9fe68f72019-08-10 19:32:03 -0700178 RunFor(dt() * 2);
Parker Schuh18dbbb42017-10-18 21:45:33 -0700179
180 basic_simulation_.VerifyResults(12.0, false);
181}
182
183// Tests that the intake is off if no intake is requested,
184// even if the limit sensor is off.
185TEST_F(BasicControlLoopTest, NoIntakeLimitNotSet) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700186 {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700187 auto builder = goal_sender_.MakeBuilder();
188 Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
189 goal_builder.add_intake(false);
190 ASSERT_TRUE(builder.Send(goal_builder.Finish()));
Austin Schuh9fe68f72019-08-10 19:32:03 -0700191 }
Parker Schuh18dbbb42017-10-18 21:45:33 -0700192 basic_simulation_.set_limit_sensor(false);
193
Austin Schuh9fe68f72019-08-10 19:32:03 -0700194 RunFor(dt() * 2);
Parker Schuh18dbbb42017-10-18 21:45:33 -0700195
196 basic_simulation_.VerifyResults(0.0, false);
197}
198
199// Tests that the intake is off even if the limit sensor
200// is pressed and intake is requested.
201TEST_F(BasicControlLoopTest, IntakeLimitSet) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700202 {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700203 auto builder = goal_sender_.MakeBuilder();
204 Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
205 goal_builder.add_intake(true);
206 ASSERT_TRUE(builder.Send(goal_builder.Finish()));
Austin Schuh9fe68f72019-08-10 19:32:03 -0700207 }
Parker Schuh18dbbb42017-10-18 21:45:33 -0700208 basic_simulation_.set_limit_sensor(true);
209
Austin Schuh9fe68f72019-08-10 19:32:03 -0700210 RunFor(dt() * 2);
Parker Schuh18dbbb42017-10-18 21:45:33 -0700211
212 basic_simulation_.VerifyResults(0.0, true);
213}
214
215// Tests that the intake is off if no intake is requested,
216TEST_F(BasicControlLoopTest, NoIntakeLimitSet) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700217 {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700218 auto builder = goal_sender_.MakeBuilder();
219 Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
220 goal_builder.add_intake(false);
221 ASSERT_TRUE(builder.Send(goal_builder.Finish()));
Austin Schuh9fe68f72019-08-10 19:32:03 -0700222 }
Parker Schuh18dbbb42017-10-18 21:45:33 -0700223 basic_simulation_.set_limit_sensor(true);
224
Austin Schuh9fe68f72019-08-10 19:32:03 -0700225 RunFor(dt() * 2);
Parker Schuh18dbbb42017-10-18 21:45:33 -0700226
227 basic_simulation_.VerifyResults(0.0, false);
228}
229
230// Tests that the control loop handles things properly if no goal is set.
231TEST_F(BasicControlLoopTest, NoGoalSet) {
232 basic_simulation_.set_limit_sensor(true);
233
Austin Schuh9fe68f72019-08-10 19:32:03 -0700234 RunFor(dt() * 2);
Parker Schuh18dbbb42017-10-18 21:45:33 -0700235
236 basic_simulation_.VerifyResults(0.0, false);
237}
238
239// Tests that the control loop handles things properly if disabled.
240TEST_F(BasicControlLoopTest, Disabled) {
241 basic_simulation_.set_limit_sensor(true);
242
Austin Schuh9fe68f72019-08-10 19:32:03 -0700243 {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700244 auto builder = goal_sender_.MakeBuilder();
245 Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
246 goal_builder.add_intake(true);
247 ASSERT_TRUE(builder.Send(goal_builder.Finish()));
Austin Schuh9fe68f72019-08-10 19:32:03 -0700248 }
Parker Schuh18dbbb42017-10-18 21:45:33 -0700249
Austin Schuh9fe68f72019-08-10 19:32:03 -0700250 SetEnabled(false);
251 RunFor(dt() * 2);
Parker Schuh18dbbb42017-10-18 21:45:33 -0700252
253 basic_simulation_.VerifyResults(0.0, false);
254}
255
256} // namespace testing
257} // namespace codelab
258} // namespace frc971