blob: 85d610e22272378a100076483f28455c16568e49 [file] [log] [blame]
#ifndef AOS_CONTROLS_CONTROL_LOOP_TEST_H_
#define AOS_CONTROLS_CONTROL_LOOP_TEST_H_
#include <chrono>
#include "gtest/gtest.h"
#include "aos/events/simulated-event-loop.h"
#include "aos/logging/queue_logging.h"
#include "aos/robot_state/robot_state.q.h"
#include "aos/testing/test_shm.h"
#include "aos/time/time.h"
namespace aos {
namespace testing {
// Handles setting up the environment that all control loops need to actually
// run.
// This includes sending the queue messages and Clear()ing the queues when
// appropriate.
// It also includes dealing with ::aos::time.
template <typename TestBaseClass>
class ControlLoopTestTemplated : public TestBaseClass {
public:
ControlLoopTestTemplated(::std::chrono::nanoseconds dt = kTimeTick)
: dt_(dt), robot_status_event_loop_(MakeEventLoop()) {
robot_state_sender_ =
robot_status_event_loop_->MakeSender<::aos::RobotState>(
".aos.robot_state");
joystick_state_sender_ =
robot_status_event_loop_->MakeSender<::aos::JoystickState>(
".aos.joystick_state");
// Schedule the robot status send 1 nanosecond before the loop runs.
send_robot_state_phased_loop_ = robot_status_event_loop_->AddPhasedLoop(
[this](int) { SendRobotState(); }, dt_,
dt - ::std::chrono::nanoseconds(1));
send_joystick_state_timer_ =
robot_status_event_loop_->AddTimer([this]() { SendJoystickState(); });
robot_status_event_loop_->OnRun([this]() {
send_joystick_state_timer_->Setup(
robot_status_event_loop_->monotonic_now(), dt_);
});
}
virtual ~ControlLoopTestTemplated() {}
void set_team_id(uint16_t team_id) { team_id_ = team_id; }
uint16_t team_id() const { return team_id_; }
// Sets the enabled/disabled bit and (potentially) rebroadcasts the robot
// state messages.
void SetEnabled(bool enabled) {
if (enabled_ != enabled) {
enabled_ = enabled;
SendJoystickState();
SendRobotState();
send_joystick_state_timer_->Setup(
robot_status_event_loop_->monotonic_now(), dt_);
}
}
// Simulate a reset of the process reading sensors, which tells loops that all
// index counts etc will be reset.
void SimulateSensorReset() {
++reader_pid_;
}
// Sets the battery voltage in robot_state.
void set_battery_voltage(double battery_voltage) {
battery_voltage_ = battery_voltage;
}
::std::unique_ptr<::aos::EventLoop> MakeEventLoop() {
return event_loop_factory_.MakeEventLoop();
}
void RunFor(monotonic_clock::duration duration) {
event_loop_factory_.RunFor(duration);
}
::aos::monotonic_clock::time_point monotonic_now() {
return event_loop_factory_.monotonic_now();
}
::std::chrono::nanoseconds dt() const { return dt_; }
private:
// Sends out all of the required queue messages.
void SendJoystickState() {
if (monotonic_now() >= kDSPacketTime + last_ds_time_ ||
last_enabled_ != enabled_) {
auto new_state = joystick_state_sender_.MakeMessage();
new_state->fake = true;
new_state->enabled = enabled_;
new_state->autonomous = false;
new_state->team_id = team_id_;
LOG_STRUCT(INFO, "joystick_state", *new_state);
new_state.Send();
last_ds_time_ = monotonic_now();
last_enabled_ = enabled_;
}
}
bool last_enabled_ = false;
void SendRobotState() {
auto new_state = robot_state_sender_.MakeMessage();
new_state->reader_pid = reader_pid_;
new_state->outputs_enabled = enabled_;
new_state->browned_out = false;
new_state->is_3v3_active = true;
new_state->is_5v_active = true;
new_state->voltage_3v3 = 3.3;
new_state->voltage_5v = 5.0;
new_state->voltage_roborio_in = battery_voltage_;
new_state->voltage_battery = battery_voltage_;
LOG_STRUCT(INFO, "robot_state", *new_state);
new_state.Send();
}
static constexpr ::std::chrono::microseconds kTimeTick{5000};
static constexpr ::std::chrono::milliseconds kDSPacketTime{20};
const ::std::chrono::nanoseconds dt_;
SimulatedEventLoopFactory event_loop_factory_;
uint16_t team_id_ = 971;
int32_t reader_pid_ = 1;
double battery_voltage_ = 12.4;
::aos::monotonic_clock::time_point last_ds_time_ =
::aos::monotonic_clock::min_time;
bool enabled_ = false;
::std::unique_ptr<::aos::EventLoop> robot_status_event_loop_;
::aos::Sender<::aos::RobotState> robot_state_sender_;
::aos::Sender<::aos::JoystickState> joystick_state_sender_;
::aos::PhasedLoopHandler *send_robot_state_phased_loop_ = nullptr;
::aos::TimerHandler *send_joystick_state_timer_ = nullptr;
};
typedef ControlLoopTestTemplated<::testing::Test> ControlLoopTest;
template <typename TestBaseClass>
constexpr ::std::chrono::microseconds
ControlLoopTestTemplated<TestBaseClass>::kTimeTick;
template <typename TestBaseClass>
constexpr ::std::chrono::milliseconds ControlLoopTestTemplated<TestBaseClass>::kDSPacketTime;
} // namespace testing
} // namespace aos
#endif // AOS_CONTROLS_CONTROL_LOOP_TEST_H_