2020-12-26 14:12:05 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2019-08-25 23:55:59 -04:00
|
|
|
|
|
|
|
|
#include "frc2/command/CommandScheduler.h"
|
|
|
|
|
|
2021-06-06 16:13:58 -07:00
|
|
|
#include <cstdio>
|
|
|
|
|
|
2020-07-06 23:32:18 -07:00
|
|
|
#include <frc/RobotBase.h>
|
2019-08-25 23:55:59 -04:00
|
|
|
#include <frc/RobotState.h>
|
2020-03-28 12:29:51 -04:00
|
|
|
#include <frc/TimedRobot.h>
|
2020-01-12 22:37:24 -08:00
|
|
|
#include <frc/livewindow/LiveWindow.h>
|
2019-12-25 00:42:14 -06:00
|
|
|
#include <hal/FRCUsageReporting.h>
|
2019-11-20 22:44:18 -08:00
|
|
|
#include <hal/HALBase.h>
|
2022-10-08 10:01:31 -07:00
|
|
|
#include <networktables/IntegerArrayTopic.h>
|
2021-06-13 16:38:05 -07:00
|
|
|
#include <networktables/NTSendableBuilder.h>
|
2022-10-08 10:01:31 -07:00
|
|
|
#include <networktables/StringArrayTopic.h>
|
2019-11-05 20:52:49 -08:00
|
|
|
#include <wpi/DenseMap.h>
|
2020-01-10 16:10:16 -08:00
|
|
|
#include <wpi/SmallVector.h>
|
2021-06-13 16:38:05 -07:00
|
|
|
#include <wpi/sendable/SendableRegistry.h>
|
2019-08-25 23:55:59 -04:00
|
|
|
|
2022-10-06 01:19:28 +03:00
|
|
|
#include "frc2/command/CommandPtr.h"
|
2019-11-05 20:52:49 -08:00
|
|
|
#include "frc2/command/Subsystem.h"
|
2019-08-25 23:55:59 -04:00
|
|
|
|
|
|
|
|
using namespace frc2;
|
2019-11-05 20:52:49 -08:00
|
|
|
|
|
|
|
|
class CommandScheduler::Impl {
|
|
|
|
|
public:
|
2022-08-30 07:53:47 +03:00
|
|
|
// A set of the currently-running commands.
|
|
|
|
|
wpi::SmallSet<Command*, 12> scheduledCommands;
|
2019-11-05 20:52:49 -08:00
|
|
|
|
|
|
|
|
// A map from required subsystems to their requiring commands. Also used as a
|
|
|
|
|
// set of the currently-required subsystems.
|
|
|
|
|
wpi::DenseMap<Subsystem*, Command*> requirements;
|
|
|
|
|
|
|
|
|
|
// A map from subsystems registered with the scheduler to their default
|
|
|
|
|
// commands. Also used as a list of currently-registered subsystems.
|
|
|
|
|
wpi::DenseMap<Subsystem*, std::unique_ptr<Command>> subsystems;
|
|
|
|
|
|
2022-06-09 08:16:51 +03:00
|
|
|
frc::EventLoop defaultButtonLoop;
|
2019-11-05 20:52:49 -08:00
|
|
|
// The set of currently-registered buttons that will be polled every
|
|
|
|
|
// iteration.
|
2022-06-09 08:16:51 +03:00
|
|
|
frc::EventLoop* activeButtonLoop{&defaultButtonLoop};
|
2019-11-05 20:52:49 -08:00
|
|
|
|
|
|
|
|
bool disabled{false};
|
|
|
|
|
|
|
|
|
|
// Lists of user-supplied actions to be executed on scheduling events for
|
|
|
|
|
// every command.
|
|
|
|
|
wpi::SmallVector<Action, 4> initActions;
|
|
|
|
|
wpi::SmallVector<Action, 4> executeActions;
|
|
|
|
|
wpi::SmallVector<Action, 4> interruptActions;
|
|
|
|
|
wpi::SmallVector<Action, 4> finishActions;
|
|
|
|
|
|
|
|
|
|
// Flag and queues for avoiding concurrent modification if commands are
|
|
|
|
|
// scheduled/canceled during run
|
|
|
|
|
|
|
|
|
|
bool inRunLoop = false;
|
2022-08-30 07:53:47 +03:00
|
|
|
wpi::SmallVector<Command*, 4> toSchedule;
|
2019-11-05 20:52:49 -08:00
|
|
|
wpi::SmallVector<Command*, 4> toCancel;
|
|
|
|
|
};
|
|
|
|
|
|
2019-08-25 23:55:59 -04:00
|
|
|
template <typename TMap, typename TKey>
|
|
|
|
|
static bool ContainsKey(const TMap& map, TKey keyToCheck) {
|
|
|
|
|
return map.find(keyToCheck) != map.end();
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 12:29:51 -04:00
|
|
|
CommandScheduler::CommandScheduler()
|
|
|
|
|
: m_impl(new Impl), m_watchdog(frc::TimedRobot::kDefaultPeriod, [] {
|
2021-06-06 16:13:58 -07:00
|
|
|
std::puts("CommandScheduler loop time overrun.");
|
2020-03-28 12:29:51 -04:00
|
|
|
}) {
|
2019-12-25 00:42:14 -06:00
|
|
|
HAL_Report(HALUsageReporting::kResourceType_Command,
|
|
|
|
|
HALUsageReporting::kCommand2_Scheduler);
|
2021-06-15 23:06:03 -07:00
|
|
|
wpi::SendableRegistry::AddLW(this, "Scheduler");
|
|
|
|
|
frc::LiveWindow::SetEnabledCallback([this] {
|
2021-05-09 18:16:07 -07:00
|
|
|
this->Disable();
|
|
|
|
|
this->CancelAll();
|
2021-06-15 23:06:03 -07:00
|
|
|
});
|
|
|
|
|
frc::LiveWindow::SetDisabledCallback([this] { this->Enable(); });
|
2019-09-14 15:22:54 -05:00
|
|
|
}
|
2019-08-25 23:55:59 -04:00
|
|
|
|
2020-01-12 22:37:24 -08:00
|
|
|
CommandScheduler::~CommandScheduler() {
|
2021-06-15 23:06:03 -07:00
|
|
|
wpi::SendableRegistry::Remove(this);
|
|
|
|
|
frc::LiveWindow::SetEnabledCallback(nullptr);
|
|
|
|
|
frc::LiveWindow::SetDisabledCallback(nullptr);
|
2020-12-31 23:35:17 -05:00
|
|
|
|
|
|
|
|
std::unique_ptr<Impl>().swap(m_impl);
|
2020-01-12 22:37:24 -08:00
|
|
|
}
|
2019-11-05 20:52:49 -08:00
|
|
|
|
2019-08-25 23:55:59 -04:00
|
|
|
CommandScheduler& CommandScheduler::GetInstance() {
|
|
|
|
|
static CommandScheduler scheduler;
|
|
|
|
|
return scheduler;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 12:29:51 -04:00
|
|
|
void CommandScheduler::SetPeriod(units::second_t period) {
|
|
|
|
|
m_watchdog.SetTimeout(period);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-09 08:16:51 +03:00
|
|
|
frc::EventLoop* CommandScheduler::GetActiveButtonLoop() const {
|
|
|
|
|
return m_impl->activeButtonLoop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandScheduler::SetActiveButtonLoop(frc::EventLoop* loop) {
|
|
|
|
|
m_impl->activeButtonLoop = loop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
frc::EventLoop* CommandScheduler::GetDefaultButtonLoop() const {
|
|
|
|
|
return &(m_impl->defaultButtonLoop);
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
void CommandScheduler::ClearButtons() {
|
2022-06-09 08:16:51 +03:00
|
|
|
m_impl->activeButtonLoop->Clear();
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2019-08-25 23:55:59 -04:00
|
|
|
|
2022-08-30 07:53:47 +03:00
|
|
|
void CommandScheduler::Schedule(Command* command) {
|
2019-11-05 20:52:49 -08:00
|
|
|
if (m_impl->inRunLoop) {
|
2022-08-30 07:53:47 +03:00
|
|
|
m_impl->toSchedule.emplace_back(command);
|
2019-10-18 10:56:12 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-07 07:13:31 +02:00
|
|
|
RequireUngrouped(command);
|
|
|
|
|
|
2022-12-13 06:29:47 +02:00
|
|
|
if (m_impl->disabled || m_impl->scheduledCommands.contains(command) ||
|
|
|
|
|
(frc::RobotState::IsDisabled() && !command->RunsWhenDisabled())) {
|
2019-08-25 23:55:59 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto& requirements = command->GetRequirements();
|
|
|
|
|
|
|
|
|
|
wpi::SmallVector<Command*, 8> intersection;
|
|
|
|
|
|
|
|
|
|
bool isDisjoint = true;
|
|
|
|
|
bool allInterruptible = true;
|
2019-11-05 20:52:49 -08:00
|
|
|
for (auto&& i1 : m_impl->requirements) {
|
2019-08-25 23:55:59 -04:00
|
|
|
if (requirements.find(i1.first) != requirements.end()) {
|
|
|
|
|
isDisjoint = false;
|
2022-08-30 07:53:47 +03:00
|
|
|
allInterruptible &= (i1.second->GetInterruptionBehavior() ==
|
|
|
|
|
Command::InterruptionBehavior::kCancelSelf);
|
2019-08-25 23:55:59 -04:00
|
|
|
intersection.emplace_back(i1.second);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isDisjoint || allInterruptible) {
|
|
|
|
|
if (allInterruptible) {
|
|
|
|
|
for (auto&& cmdToCancel : intersection) {
|
|
|
|
|
Cancel(cmdToCancel);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-30 07:53:47 +03:00
|
|
|
m_impl->scheduledCommands.insert(command);
|
2019-08-25 23:55:59 -04:00
|
|
|
for (auto&& requirement : requirements) {
|
2019-11-05 20:52:49 -08:00
|
|
|
m_impl->requirements[requirement] = command;
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
2022-06-16 09:32:16 +03:00
|
|
|
command->Initialize();
|
2020-05-21 07:00:34 +03:00
|
|
|
for (auto&& action : m_impl->initActions) {
|
|
|
|
|
action(*command);
|
|
|
|
|
}
|
2020-03-28 12:29:51 -04:00
|
|
|
m_watchdog.AddEpoch(command->GetName() + ".Initialize()");
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-15 16:33:14 -07:00
|
|
|
void CommandScheduler::Schedule(std::span<Command* const> commands) {
|
2019-08-25 23:55:59 -04:00
|
|
|
for (auto command : commands) {
|
2022-08-30 07:53:47 +03:00
|
|
|
Schedule(command);
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandScheduler::Schedule(std::initializer_list<Command*> commands) {
|
|
|
|
|
for (auto command : commands) {
|
2022-08-30 07:53:47 +03:00
|
|
|
Schedule(command);
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-06 01:19:28 +03:00
|
|
|
void CommandScheduler::Schedule(const CommandPtr& command) {
|
|
|
|
|
Schedule(command.get());
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-25 23:55:59 -04:00
|
|
|
void CommandScheduler::Run() {
|
2019-11-05 20:52:49 -08:00
|
|
|
if (m_impl->disabled) {
|
2019-08-25 23:55:59 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 12:29:51 -04:00
|
|
|
m_watchdog.Reset();
|
|
|
|
|
|
2019-08-25 23:55:59 -04:00
|
|
|
// Run the periodic method of all registered subsystems.
|
2019-11-05 20:52:49 -08:00
|
|
|
for (auto&& subsystem : m_impl->subsystems) {
|
2019-08-25 23:55:59 -04:00
|
|
|
subsystem.getFirst()->Periodic();
|
2020-10-03 22:26:19 -07:00
|
|
|
if constexpr (frc::RobotBase::IsSimulation()) {
|
2020-07-06 23:32:18 -07:00
|
|
|
subsystem.getFirst()->SimulationPeriodic();
|
|
|
|
|
}
|
2020-03-28 12:29:51 -04:00
|
|
|
m_watchdog.AddEpoch("Subsystem Periodic()");
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
|
2022-06-09 08:16:51 +03:00
|
|
|
// Cache the active instance to avoid concurrency problems if SetActiveLoop()
|
|
|
|
|
// is called from inside the button bindings.
|
|
|
|
|
frc::EventLoop* loopCache = m_impl->activeButtonLoop;
|
2019-08-25 23:55:59 -04:00
|
|
|
// Poll buttons for new commands to add.
|
2022-06-09 08:16:51 +03:00
|
|
|
loopCache->Poll();
|
2020-03-28 12:29:51 -04:00
|
|
|
m_watchdog.AddEpoch("buttons.Run()");
|
2019-08-25 23:55:59 -04:00
|
|
|
|
2019-11-05 20:52:49 -08:00
|
|
|
m_impl->inRunLoop = true;
|
2019-08-25 23:55:59 -04:00
|
|
|
// Run scheduled commands, remove finished commands.
|
2022-08-30 07:53:47 +03:00
|
|
|
for (Command* command : m_impl->scheduledCommands) {
|
2019-08-25 23:55:59 -04:00
|
|
|
if (!command->RunsWhenDisabled() && frc::RobotState::IsDisabled()) {
|
|
|
|
|
Cancel(command);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
command->Execute();
|
2019-11-05 20:52:49 -08:00
|
|
|
for (auto&& action : m_impl->executeActions) {
|
2019-08-25 23:55:59 -04:00
|
|
|
action(*command);
|
|
|
|
|
}
|
2020-03-28 12:29:51 -04:00
|
|
|
m_watchdog.AddEpoch(command->GetName() + ".Execute()");
|
2019-08-25 23:55:59 -04:00
|
|
|
|
|
|
|
|
if (command->IsFinished()) {
|
|
|
|
|
command->End(false);
|
2019-11-05 20:52:49 -08:00
|
|
|
for (auto&& action : m_impl->finishActions) {
|
2019-08-25 23:55:59 -04:00
|
|
|
action(*command);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto&& requirement : command->GetRequirements()) {
|
2019-11-05 20:52:49 -08:00
|
|
|
m_impl->requirements.erase(requirement);
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 07:53:47 +03:00
|
|
|
m_impl->scheduledCommands.erase(command);
|
2020-03-28 12:29:51 -04:00
|
|
|
m_watchdog.AddEpoch(command->GetName() + ".End(false)");
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
}
|
2019-11-05 20:52:49 -08:00
|
|
|
m_impl->inRunLoop = false;
|
2019-10-18 10:56:12 -04:00
|
|
|
|
2022-08-30 07:53:47 +03:00
|
|
|
for (Command* command : m_impl->toSchedule) {
|
|
|
|
|
Schedule(command);
|
2019-10-18 10:56:12 -04:00
|
|
|
}
|
|
|
|
|
|
2019-11-05 20:52:49 -08:00
|
|
|
for (auto&& command : m_impl->toCancel) {
|
2019-10-18 10:56:12 -04:00
|
|
|
Cancel(command);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-05 20:52:49 -08:00
|
|
|
m_impl->toSchedule.clear();
|
|
|
|
|
m_impl->toCancel.clear();
|
2019-08-25 23:55:59 -04:00
|
|
|
|
|
|
|
|
// Add default commands for un-required registered subsystems.
|
2019-11-05 20:52:49 -08:00
|
|
|
for (auto&& subsystem : m_impl->subsystems) {
|
|
|
|
|
auto s = m_impl->requirements.find(subsystem.getFirst());
|
|
|
|
|
if (s == m_impl->requirements.end() && subsystem.getSecond()) {
|
2019-08-25 23:55:59 -04:00
|
|
|
Schedule({subsystem.getSecond().get()});
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-28 12:29:51 -04:00
|
|
|
|
|
|
|
|
m_watchdog.Disable();
|
|
|
|
|
if (m_watchdog.IsExpired()) {
|
|
|
|
|
m_watchdog.PrintEpochs();
|
|
|
|
|
}
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandScheduler::RegisterSubsystem(Subsystem* subsystem) {
|
2022-11-28 16:03:14 -06:00
|
|
|
if (m_impl->subsystems.find(subsystem) != m_impl->subsystems.end()) {
|
|
|
|
|
std::puts("Tried to register an already-registered subsystem");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-05 20:52:49 -08:00
|
|
|
m_impl->subsystems[subsystem] = nullptr;
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandScheduler::UnregisterSubsystem(Subsystem* subsystem) {
|
2019-11-05 20:52:49 -08:00
|
|
|
auto s = m_impl->subsystems.find(subsystem);
|
|
|
|
|
if (s != m_impl->subsystems.end()) {
|
|
|
|
|
m_impl->subsystems.erase(s);
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandScheduler::RegisterSubsystem(
|
|
|
|
|
std::initializer_list<Subsystem*> subsystems) {
|
|
|
|
|
for (auto* subsystem : subsystems) {
|
|
|
|
|
RegisterSubsystem(subsystem);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-06 19:51:14 -07:00
|
|
|
void CommandScheduler::RegisterSubsystem(
|
2022-10-15 16:33:14 -07:00
|
|
|
std::span<Subsystem* const> subsystems) {
|
2020-01-01 20:09:17 -08:00
|
|
|
for (auto* subsystem : subsystems) {
|
|
|
|
|
RegisterSubsystem(subsystem);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-25 23:55:59 -04:00
|
|
|
void CommandScheduler::UnregisterSubsystem(
|
|
|
|
|
std::initializer_list<Subsystem*> subsystems) {
|
|
|
|
|
for (auto* subsystem : subsystems) {
|
|
|
|
|
UnregisterSubsystem(subsystem);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-01 20:09:17 -08:00
|
|
|
void CommandScheduler::UnregisterSubsystem(
|
2022-10-15 16:33:14 -07:00
|
|
|
std::span<Subsystem* const> subsystems) {
|
2020-01-01 20:09:17 -08:00
|
|
|
for (auto* subsystem : subsystems) {
|
|
|
|
|
UnregisterSubsystem(subsystem);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-21 16:35:58 +03:00
|
|
|
void CommandScheduler::SetDefaultCommand(Subsystem* subsystem,
|
|
|
|
|
CommandPtr&& defaultCommand) {
|
|
|
|
|
if (!defaultCommand.get()->HasRequirement(subsystem)) {
|
|
|
|
|
throw FRC_MakeError(frc::err::CommandIllegalUse, "{}",
|
|
|
|
|
"Default commands must require their subsystem!");
|
|
|
|
|
}
|
2022-12-07 07:13:31 +02:00
|
|
|
RequireUngrouped(defaultCommand.get());
|
2022-10-21 16:35:58 +03:00
|
|
|
|
|
|
|
|
SetDefaultCommandImpl(subsystem, std::move(defaultCommand).Unwrap());
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-28 16:03:14 -06:00
|
|
|
void CommandScheduler::RemoveDefaultCommand(Subsystem* subsystem) {
|
|
|
|
|
m_impl->subsystems[subsystem] = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-25 23:55:59 -04:00
|
|
|
Command* CommandScheduler::GetDefaultCommand(const Subsystem* subsystem) const {
|
2019-11-05 20:52:49 -08:00
|
|
|
auto&& find = m_impl->subsystems.find(subsystem);
|
|
|
|
|
if (find != m_impl->subsystems.end()) {
|
2019-08-25 23:55:59 -04:00
|
|
|
return find->second.get();
|
|
|
|
|
} else {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandScheduler::Cancel(Command* command) {
|
2020-12-31 23:35:17 -05:00
|
|
|
if (!m_impl) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-05 20:52:49 -08:00
|
|
|
if (m_impl->inRunLoop) {
|
|
|
|
|
m_impl->toCancel.emplace_back(command);
|
2019-10-18 10:56:12 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-05 20:52:49 -08:00
|
|
|
auto find = m_impl->scheduledCommands.find(command);
|
2020-12-28 12:58:06 -08:00
|
|
|
if (find == m_impl->scheduledCommands.end()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-08-30 07:53:47 +03:00
|
|
|
m_impl->scheduledCommands.erase(*find);
|
2019-11-05 20:52:49 -08:00
|
|
|
for (auto&& requirement : m_impl->requirements) {
|
2019-08-25 23:55:59 -04:00
|
|
|
if (requirement.second == command) {
|
2019-11-05 20:52:49 -08:00
|
|
|
m_impl->requirements.erase(requirement.first);
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
}
|
2022-06-16 09:32:16 +03:00
|
|
|
command->End(true);
|
|
|
|
|
for (auto&& action : m_impl->interruptActions) {
|
|
|
|
|
action(*command);
|
|
|
|
|
}
|
|
|
|
|
m_watchdog.AddEpoch(command->GetName() + ".End(true)");
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
|
2022-10-06 01:19:28 +03:00
|
|
|
void CommandScheduler::Cancel(const CommandPtr& command) {
|
|
|
|
|
Cancel(command.get());
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-15 16:33:14 -07:00
|
|
|
void CommandScheduler::Cancel(std::span<Command* const> commands) {
|
2019-08-25 23:55:59 -04:00
|
|
|
for (auto command : commands) {
|
|
|
|
|
Cancel(command);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandScheduler::Cancel(std::initializer_list<Command*> commands) {
|
|
|
|
|
for (auto command : commands) {
|
|
|
|
|
Cancel(command);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandScheduler::CancelAll() {
|
2020-01-10 16:10:16 -08:00
|
|
|
wpi::SmallVector<Command*, 16> commands;
|
2019-11-05 20:52:49 -08:00
|
|
|
for (auto&& command : m_impl->scheduledCommands) {
|
2022-08-30 07:53:47 +03:00
|
|
|
commands.emplace_back(command);
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
2020-01-10 16:10:16 -08:00
|
|
|
Cancel(commands);
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CommandScheduler::IsScheduled(
|
2022-10-15 16:33:14 -07:00
|
|
|
std::span<const Command* const> commands) const {
|
2019-08-25 23:55:59 -04:00
|
|
|
for (auto command : commands) {
|
|
|
|
|
if (!IsScheduled(command)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CommandScheduler::IsScheduled(
|
|
|
|
|
std::initializer_list<const Command*> commands) const {
|
|
|
|
|
for (auto command : commands) {
|
|
|
|
|
if (!IsScheduled(command)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CommandScheduler::IsScheduled(const Command* command) const {
|
2022-08-30 07:53:47 +03:00
|
|
|
return m_impl->scheduledCommands.contains(command);
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
|
2022-10-06 01:19:28 +03:00
|
|
|
bool CommandScheduler::IsScheduled(const CommandPtr& command) const {
|
|
|
|
|
return m_impl->scheduledCommands.contains(command.get());
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-25 23:55:59 -04:00
|
|
|
Command* CommandScheduler::Requiring(const Subsystem* subsystem) const {
|
2019-11-05 20:52:49 -08:00
|
|
|
auto find = m_impl->requirements.find(subsystem);
|
|
|
|
|
if (find != m_impl->requirements.end()) {
|
2019-08-25 23:55:59 -04:00
|
|
|
return find->second;
|
|
|
|
|
} else {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
void CommandScheduler::Disable() {
|
|
|
|
|
m_impl->disabled = true;
|
|
|
|
|
}
|
2019-08-25 23:55:59 -04:00
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
void CommandScheduler::Enable() {
|
|
|
|
|
m_impl->disabled = false;
|
|
|
|
|
}
|
2019-08-25 23:55:59 -04:00
|
|
|
|
|
|
|
|
void CommandScheduler::OnCommandInitialize(Action action) {
|
2019-11-05 20:52:49 -08:00
|
|
|
m_impl->initActions.emplace_back(std::move(action));
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandScheduler::OnCommandExecute(Action action) {
|
2019-11-05 20:52:49 -08:00
|
|
|
m_impl->executeActions.emplace_back(std::move(action));
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandScheduler::OnCommandInterrupt(Action action) {
|
2019-11-05 20:52:49 -08:00
|
|
|
m_impl->interruptActions.emplace_back(std::move(action));
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandScheduler::OnCommandFinish(Action action) {
|
2019-11-05 20:52:49 -08:00
|
|
|
m_impl->finishActions.emplace_back(std::move(action));
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
|
|
|
|
|
2022-12-07 07:13:31 +02:00
|
|
|
void CommandScheduler::RequireUngrouped(const Command* command) {
|
|
|
|
|
if (command->IsComposed()) {
|
2023-01-12 08:53:04 +02:00
|
|
|
throw FRC_MakeError(frc::err::CommandIllegalUse,
|
|
|
|
|
"Commands that have been composed may not be added to "
|
|
|
|
|
"another composition or scheduled "
|
|
|
|
|
"individually!");
|
2022-12-07 07:13:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandScheduler::RequireUngrouped(
|
|
|
|
|
std::span<const std::unique_ptr<Command>> commands) {
|
|
|
|
|
for (auto&& command : commands) {
|
|
|
|
|
RequireUngrouped(command.get());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CommandScheduler::RequireUngrouped(
|
|
|
|
|
std::initializer_list<const Command*> commands) {
|
|
|
|
|
for (auto&& command : commands) {
|
|
|
|
|
RequireUngrouped(command);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-13 16:38:05 -07:00
|
|
|
void CommandScheduler::InitSendable(nt::NTSendableBuilder& builder) {
|
2019-08-25 23:55:59 -04:00
|
|
|
builder.SetSmartDashboardType("Scheduler");
|
2022-10-08 10:01:31 -07:00
|
|
|
builder.SetUpdateTable(
|
|
|
|
|
[this,
|
|
|
|
|
namesPub = nt::StringArrayTopic{builder.GetTopic("Names")}.Publish(),
|
|
|
|
|
idsPub = nt::IntegerArrayTopic{builder.GetTopic("Ids")}.Publish(),
|
|
|
|
|
cancelEntry = nt::IntegerArrayTopic{builder.GetTopic("Cancel")}.GetEntry(
|
|
|
|
|
{})]() mutable {
|
|
|
|
|
auto toCancel = cancelEntry.Get();
|
|
|
|
|
if (!toCancel.empty()) {
|
|
|
|
|
for (auto cancel : cancelEntry.Get()) {
|
|
|
|
|
uintptr_t ptrTmp = static_cast<uintptr_t>(cancel);
|
|
|
|
|
Command* command = reinterpret_cast<Command*>(ptrTmp);
|
|
|
|
|
if (m_impl->scheduledCommands.find(command) !=
|
|
|
|
|
m_impl->scheduledCommands.end()) {
|
|
|
|
|
Cancel(command);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
cancelEntry.Set({});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wpi::SmallVector<std::string, 8> names;
|
|
|
|
|
wpi::SmallVector<int64_t, 8> ids;
|
|
|
|
|
for (Command* command : m_impl->scheduledCommands) {
|
|
|
|
|
names.emplace_back(command->GetName());
|
|
|
|
|
uintptr_t ptrTmp = reinterpret_cast<uintptr_t>(command);
|
|
|
|
|
ids.emplace_back(static_cast<int64_t>(ptrTmp));
|
|
|
|
|
}
|
|
|
|
|
namesPub.Set(names);
|
|
|
|
|
idsPub.Set(ids);
|
|
|
|
|
});
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
2019-11-05 20:52:49 -08:00
|
|
|
|
|
|
|
|
void CommandScheduler::SetDefaultCommandImpl(Subsystem* subsystem,
|
|
|
|
|
std::unique_ptr<Command> command) {
|
2022-08-30 07:53:47 +03:00
|
|
|
if (command->GetInterruptionBehavior() ==
|
|
|
|
|
Command::InterruptionBehavior::kCancelIncoming) {
|
|
|
|
|
std::puts(
|
|
|
|
|
"Registering a non-interruptible default command!\n"
|
|
|
|
|
"This will likely prevent any other commands from "
|
|
|
|
|
"requiring this subsystem.");
|
|
|
|
|
// Warn, but allow -- there might be a use case for this.
|
|
|
|
|
}
|
2019-11-05 20:52:49 -08:00
|
|
|
m_impl->subsystems[subsystem] = std::move(command);
|
|
|
|
|
}
|