blob: 6a8e97573cb9246b19c06cf60aef558f33aedade [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#ifndef AOS_CONTROLS_CONTROL_LOOP_TEST_H_
2#define AOS_CONTROLS_CONTROL_LOOP_TEST_H_
Brian Silverman65e49702014-04-30 17:36:40 -07003
Austin Schuh2001aa42018-10-29 22:57:02 -07004#include <chrono>
5
Brian Silverman65e49702014-04-30 17:36:40 -07006#include "gtest/gtest.h"
7
Alex Perrycb7da4b2019-08-28 19:35:56 -07008#include "aos/events/simulated_event_loop.h"
9#include "aos/flatbuffers.h"
10#include "aos/json_to_flatbuffer.h"
11#include "aos/robot_state/joystick_state_generated.h"
12#include "aos/robot_state/robot_state_generated.h"
13#include "aos/testing/test_logging.h"
John Park33858a32018-09-28 23:05:48 -070014#include "aos/time/time.h"
Brian Silverman65e49702014-04-30 17:36:40 -070015
16namespace aos {
17namespace testing {
18
19// Handles setting up the environment that all control loops need to actually
20// run.
21// This includes sending the queue messages and Clear()ing the queues when
22// appropriate.
23// It also includes dealing with ::aos::time.
Austin Schuh2001aa42018-10-29 22:57:02 -070024template <typename TestBaseClass>
25class ControlLoopTestTemplated : public TestBaseClass {
Brian Silverman65e49702014-04-30 17:36:40 -070026 public:
Alex Perrycb7da4b2019-08-28 19:35:56 -070027 // Builds a control loop test with the provided configuration. Note: this
28 // merges and sorts the config to reduce user errors.
James Kuszmaul3ae42262019-11-08 12:33:41 -080029 ControlLoopTestTemplated(std::string_view configuration,
Alex Perrycb7da4b2019-08-28 19:35:56 -070030 ::std::chrono::nanoseconds dt = kTimeTick)
31 : ControlLoopTestTemplated(
32 configuration::MergeConfiguration(
33 aos::FlatbufferDetachedBuffer<Configuration>(JsonToFlatbuffer(
34 configuration, Configuration::MiniReflectTypeTable()))),
35 dt) {}
36
37 ControlLoopTestTemplated(
38 FlatbufferDetachedBuffer<Configuration> configuration,
39 ::std::chrono::nanoseconds dt = kTimeTick)
40 : configuration_(std::move(configuration)),
41 event_loop_factory_(&configuration_.message()),
42 dt_(dt),
43 robot_status_event_loop_(MakeEventLoop()) {
44 testing::EnableTestLogging();
Austin Schuhdf6cbb12019-02-02 13:46:52 -080045 robot_state_sender_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -070046 robot_status_event_loop_->MakeSender<::aos::RobotState>("/aos");
Austin Schuhdf6cbb12019-02-02 13:46:52 -080047 joystick_state_sender_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -070048 robot_status_event_loop_->MakeSender<::aos::JoystickState>("/aos");
Austin Schuh2001aa42018-10-29 22:57:02 -070049
Austin Schuh9fe68f72019-08-10 19:32:03 -070050 // Schedule the robot status send 1 nanosecond before the loop runs.
51 send_robot_state_phased_loop_ = robot_status_event_loop_->AddPhasedLoop(
52 [this](int) { SendRobotState(); }, dt_,
53 dt - ::std::chrono::nanoseconds(1));
Austin Schuh2001aa42018-10-29 22:57:02 -070054
Austin Schuh9fe68f72019-08-10 19:32:03 -070055 send_joystick_state_timer_ =
56 robot_status_event_loop_->AddTimer([this]() { SendJoystickState(); });
57
58 robot_status_event_loop_->OnRun([this]() {
59 send_joystick_state_timer_->Setup(
60 robot_status_event_loop_->monotonic_now(), dt_);
61 });
Austin Schuh2001aa42018-10-29 22:57:02 -070062 }
Austin Schuh9fe68f72019-08-10 19:32:03 -070063 virtual ~ControlLoopTestTemplated() {}
Brian Silverman65e49702014-04-30 17:36:40 -070064
Philipp Schraderf75a8bf2015-02-02 05:30:16 +000065 void set_team_id(uint16_t team_id) { team_id_ = team_id; }
66 uint16_t team_id() const { return team_id_; }
67
Austin Schuh9fe68f72019-08-10 19:32:03 -070068 // Sets the enabled/disabled bit and (potentially) rebroadcasts the robot
69 // state messages.
70 void SetEnabled(bool enabled) {
71 if (enabled_ != enabled) {
72 enabled_ = enabled;
73 SendJoystickState();
74 SendRobotState();
75 send_joystick_state_timer_->Setup(
76 robot_status_event_loop_->monotonic_now(), dt_);
Austin Schuh2001aa42018-10-29 22:57:02 -070077 }
Brian Silvermane6f64ab2015-02-05 17:03:56 -050078 }
Brian Silverman65e49702014-04-30 17:36:40 -070079
Brian Silverman57cad222015-02-14 20:46:41 -050080 // Simulate a reset of the process reading sensors, which tells loops that all
81 // index counts etc will be reset.
82 void SimulateSensorReset() {
83 ++reader_pid_;
84 }
85
Austin Schuhe5f064d2016-03-05 17:43:51 -080086 // Sets the battery voltage in robot_state.
87 void set_battery_voltage(double battery_voltage) {
88 battery_voltage_ = battery_voltage;
89 }
90
Austin Schuh9fe68f72019-08-10 19:32:03 -070091 ::std::unique_ptr<::aos::EventLoop> MakeEventLoop() {
92 return event_loop_factory_.MakeEventLoop();
93 }
94
95 void RunFor(monotonic_clock::duration duration) {
96 event_loop_factory_.RunFor(duration);
97 }
98
99 ::aos::monotonic_clock::time_point monotonic_now() {
100 return event_loop_factory_.monotonic_now();
101 }
102
103 ::std::chrono::nanoseconds dt() const { return dt_; }
104
Brian Silverman65e49702014-04-30 17:36:40 -0700105 private:
Austin Schuh9fe68f72019-08-10 19:32:03 -0700106 // Sends out all of the required queue messages.
107 void SendJoystickState() {
108 if (monotonic_now() >= kDSPacketTime + last_ds_time_ ||
109 last_enabled_ != enabled_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700110 auto new_state = joystick_state_sender_.MakeBuilder();
111 ::aos::JoystickState::Builder builder =
112 new_state.template MakeBuilder<::aos::JoystickState>();
Austin Schuh9fe68f72019-08-10 19:32:03 -0700113
Alex Perrycb7da4b2019-08-28 19:35:56 -0700114 builder.add_fake(true);
Austin Schuh9fe68f72019-08-10 19:32:03 -0700115
Alex Perrycb7da4b2019-08-28 19:35:56 -0700116 builder.add_enabled(enabled_);
117 builder.add_autonomous(false);
118 builder.add_team_id(team_id_);
119
120 new_state.Send(builder.Finish());
Austin Schuh9fe68f72019-08-10 19:32:03 -0700121
122 last_ds_time_ = monotonic_now();
123 last_enabled_ = enabled_;
124 }
125 }
126
127 bool last_enabled_ = false;
128
129 void SendRobotState() {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700130 auto new_state = robot_state_sender_.MakeBuilder();
Austin Schuh9fe68f72019-08-10 19:32:03 -0700131
Alex Perrycb7da4b2019-08-28 19:35:56 -0700132 ::aos::RobotState::Builder builder =
133 new_state.template MakeBuilder<::aos::RobotState>();
Austin Schuh9fe68f72019-08-10 19:32:03 -0700134
Alex Perrycb7da4b2019-08-28 19:35:56 -0700135 builder.add_reader_pid(reader_pid_);
136 builder.add_outputs_enabled(enabled_);
137 builder.add_browned_out(false);
Austin Schuh9fe68f72019-08-10 19:32:03 -0700138
Alex Perrycb7da4b2019-08-28 19:35:56 -0700139 builder.add_is_3v3_active(true);
140 builder.add_is_5v_active(true);
141 builder.add_voltage_3v3(3.3);
142 builder.add_voltage_5v(5.0);
Austin Schuh9fe68f72019-08-10 19:32:03 -0700143
Alex Perrycb7da4b2019-08-28 19:35:56 -0700144 builder.add_voltage_roborio_in(battery_voltage_);
145 builder.add_voltage_battery(battery_voltage_);
146
147 new_state.Send(builder.Finish());
Austin Schuh9fe68f72019-08-10 19:32:03 -0700148 }
149
150 static constexpr ::std::chrono::microseconds kTimeTick{5000};
Austin Schuh6a6f90c2016-11-25 21:36:42 -0800151 static constexpr ::std::chrono::milliseconds kDSPacketTime{20};
Brian Silvermane6f64ab2015-02-05 17:03:56 -0500152
Alex Perrycb7da4b2019-08-28 19:35:56 -0700153 FlatbufferDetachedBuffer<Configuration> configuration_;
Austin Schuh9fe68f72019-08-10 19:32:03 -0700154
155 SimulatedEventLoopFactory event_loop_factory_;
156
Alex Perrycb7da4b2019-08-28 19:35:56 -0700157 const ::std::chrono::nanoseconds dt_;
158
Philipp Schraderf75a8bf2015-02-02 05:30:16 +0000159 uint16_t team_id_ = 971;
Brian Silverman57cad222015-02-14 20:46:41 -0500160 int32_t reader_pid_ = 1;
Austin Schuhe5f064d2016-03-05 17:43:51 -0800161 double battery_voltage_ = 12.4;
Philipp Schraderf75a8bf2015-02-02 05:30:16 +0000162
Austin Schuh6a6f90c2016-11-25 21:36:42 -0800163 ::aos::monotonic_clock::time_point last_ds_time_ =
Austin Schuh9fe68f72019-08-10 19:32:03 -0700164 ::aos::monotonic_clock::min_time;
Brian Silverman65e49702014-04-30 17:36:40 -0700165
Austin Schuh9fe68f72019-08-10 19:32:03 -0700166 bool enabled_ = false;
Campbell Crowley152c7cf2016-02-14 21:20:50 -0800167
Austin Schuh9fe68f72019-08-10 19:32:03 -0700168 ::std::unique_ptr<::aos::EventLoop> robot_status_event_loop_;
Austin Schuhdf6cbb12019-02-02 13:46:52 -0800169
170 ::aos::Sender<::aos::RobotState> robot_state_sender_;
171 ::aos::Sender<::aos::JoystickState> joystick_state_sender_;
Austin Schuh9fe68f72019-08-10 19:32:03 -0700172
173 ::aos::PhasedLoopHandler *send_robot_state_phased_loop_ = nullptr;
174 ::aos::TimerHandler *send_joystick_state_timer_ = nullptr;
Brian Silverman65e49702014-04-30 17:36:40 -0700175};
176
Austin Schuh2001aa42018-10-29 22:57:02 -0700177typedef ControlLoopTestTemplated<::testing::Test> ControlLoopTest;
178
179template <typename TestBaseClass>
Austin Schuh9fe68f72019-08-10 19:32:03 -0700180constexpr ::std::chrono::microseconds
181 ControlLoopTestTemplated<TestBaseClass>::kTimeTick;
Austin Schuh2001aa42018-10-29 22:57:02 -0700182
183template <typename TestBaseClass>
184constexpr ::std::chrono::milliseconds ControlLoopTestTemplated<TestBaseClass>::kDSPacketTime;
185
Brian Silverman65e49702014-04-30 17:36:40 -0700186} // namespace testing
187} // namespace aos
188
John Park33858a32018-09-28 23:05:48 -0700189#endif // AOS_CONTROLS_CONTROL_LOOP_TEST_H_