blob: 4b3e04836639939ed033997b2d3bcbf2439ee2da [file] [log] [blame]
#ifndef FRC971_ACTIONS_ACTION_CLIENT_H_
#define FRC971_ACTIONS_ACTION_CLIENT_H_
#include <type_traits>
#include "aos/common/logging/logging.h"
#include "aos/common/queue.h"
namespace frc971 {
class Action {
public:
// Cancels the action.
void Cancel() { DoCancel(); }
// Returns true if the action is currently running.
bool Running() { return DoRunning(); }
// Starts the action.
void Start() { DoStart(); }
// Waits until the action has finished.
void WaitUntilDone() { DoWaitUntilDone(); }
virtual ~Action() {}
private:
virtual void DoCancel() = 0;
virtual bool DoRunning() = 0;
virtual void DoStart() = 0;
virtual void DoWaitUntilDone() = 0;
};
// Templated subclass to hold the type information.
template <typename T>
class TypedAction : public Action {
public:
typedef typename std::remove_reference<
decltype(*(static_cast<T *>(NULL)->goal.MakeMessage().get()))>::type
GoalType;
TypedAction(T *queue_group)
: queue_group_(queue_group),
goal_(queue_group_->goal.MakeMessage()),
has_started_(false) {}
// Returns the current goal that will be sent when the action is sent.
GoalType *GetGoal() { return goal_.get(); }
virtual ~TypedAction() {
LOG(INFO, "Calling destructor\n");
DoCancel();
}
private:
// Cancels the action.
virtual void DoCancel() {
LOG(INFO, "Canceling action on queue %s\n", queue_group_->goal.name());
queue_group_->goal.MakeWithBuilder().run(false).Send();
}
// Returns true if the action is running or we don't have an initial response
// back from it to signal whether or not it is running.
virtual bool DoRunning() {
if (has_started_) {
queue_group_->status.FetchLatest();
} else if (queue_group_->status.FetchLatest()) {
if (queue_group_->status->running) {
// Wait until it reports that it is running to start.
has_started_ = true;
}
}
return !has_started_ ||
(queue_group_->status.get() && queue_group_->status->running);
}
// Returns true if the action is running or we don't have an initial response
// back from it to signal whether or not it is running.
virtual void DoWaitUntilDone() {
queue_group_->status.FetchLatest();
while (true) {
if (has_started_) {
queue_group_->status.FetchNextBlocking();
} else {
if (queue_group_->status->running) {
has_started_ = true;
}
queue_group_->status.FetchNextBlocking();
if (queue_group_->status->running) {
// Wait until it reports that it is running to start.
has_started_ = true;
}
}
if (has_started_ &&
(queue_group_->status.get() && !queue_group_->status->running)) {
return;
}
}
}
// Starts the action if a goal has been created.
virtual void DoStart() {
if (goal_) {
goal_->run = true;
goal_.Send();
has_started_ = false;
LOG(INFO, "Starting action\n");
} else {
has_started_ = true;
}
}
T *queue_group_;
::aos::ScopedMessagePtr<GoalType> goal_;
// Track if we have seen a response to the start message.
// If we haven't, we are considered running regardless.
bool has_started_;
};
} // namespace frc971
#endif // FRC971_ACTIONS_ACTION_CLIENT_H_