blob: aa325b4921997efef21117db96627f1a4f90f524 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2011-2017. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "Commands/Scheduler.h"
9
10#include <algorithm>
11#include <iostream>
12#include <set>
13
14#include "Buttons/ButtonScheduler.h"
15#include "Commands/Subsystem.h"
16#include "HLUsageReporting.h"
17#include "WPIErrors.h"
18
19using namespace frc;
20
21Scheduler::Scheduler() { HLUsageReporting::ReportScheduler(); }
22
23/**
24 * Returns the {@link Scheduler}, creating it if one does not exist.
25 *
26 * @return the {@link Scheduler}
27 */
28Scheduler* Scheduler::GetInstance() {
29 static Scheduler instance;
30 return &instance;
31}
32
33void Scheduler::SetEnabled(bool enabled) { m_enabled = enabled; }
34
35/**
36 * Add a command to be scheduled later.
37 *
38 * In any pass through the scheduler, all commands are added to the additions
39 * list, then at the end of the pass, they are all scheduled.
40 *
41 * @param command The command to be scheduled
42 */
43void Scheduler::AddCommand(Command* command) {
44 std::lock_guard<priority_mutex> sync(m_additionsLock);
45 if (std::find(m_additions.begin(), m_additions.end(), command) !=
46 m_additions.end())
47 return;
48 m_additions.push_back(command);
49}
50
51void Scheduler::AddButton(ButtonScheduler* button) {
52 std::lock_guard<priority_mutex> sync(m_buttonsLock);
53 m_buttons.push_back(button);
54}
55
56void Scheduler::ProcessCommandAddition(Command* command) {
57 if (command == nullptr) return;
58
59 // Check to make sure no adding during adding
60 if (m_adding) {
61 wpi_setWPIErrorWithContext(IncompatibleState,
62 "Can not start command from cancel method");
63 return;
64 }
65
66 // Only add if not already in
67 auto found = m_commands.find(command);
68 if (found == m_commands.end()) {
69 // Check that the requirements can be had
70 Command::SubsystemSet requirements = command->GetRequirements();
71 Command::SubsystemSet::iterator iter;
72 for (iter = requirements.begin(); iter != requirements.end(); iter++) {
73 Subsystem* lock = *iter;
74 if (lock->GetCurrentCommand() != nullptr &&
75 !lock->GetCurrentCommand()->IsInterruptible())
76 return;
77 }
78
79 // Give it the requirements
80 m_adding = true;
81 for (iter = requirements.begin(); iter != requirements.end(); iter++) {
82 Subsystem* lock = *iter;
83 if (lock->GetCurrentCommand() != nullptr) {
84 lock->GetCurrentCommand()->Cancel();
85 Remove(lock->GetCurrentCommand());
86 }
87 lock->SetCurrentCommand(command);
88 }
89 m_adding = false;
90
91 m_commands.insert(command);
92
93 command->StartRunning();
94 m_runningCommandsChanged = true;
95 }
96}
97
98/**
99 * Runs a single iteration of the loop.
100 *
101 * This method should be called often in order to have a functioning
102 * {@link Command} system. The loop has five stages:
103 *
104 * <ol>
105 * <li> Poll the Buttons </li>
106 * <li> Execute/Remove the Commands </li>
107 * <li> Send values to SmartDashboard </li>
108 * <li> Add Commands </li>
109 * <li> Add Defaults </li>
110 * </ol>
111 */
112void Scheduler::Run() {
113 // Get button input (going backwards preserves button priority)
114 {
115 if (!m_enabled) return;
116
117 std::lock_guard<priority_mutex> sync(m_buttonsLock);
118 auto rButtonIter = m_buttons.rbegin();
119 for (; rButtonIter != m_buttons.rend(); rButtonIter++) {
120 (*rButtonIter)->Execute();
121 }
122 }
123
124 m_runningCommandsChanged = false;
125
126 // Loop through the commands
127 auto commandIter = m_commands.begin();
128 for (; commandIter != m_commands.end();) {
129 Command* command = *commandIter;
130 // Increment before potentially removing to keep the iterator valid
131 commandIter++;
132 if (!command->Run()) {
133 Remove(command);
134 m_runningCommandsChanged = true;
135 }
136 }
137
138 // Add the new things
139 {
140 std::lock_guard<priority_mutex> sync(m_additionsLock);
141 auto additionsIter = m_additions.begin();
142 for (; additionsIter != m_additions.end(); additionsIter++) {
143 ProcessCommandAddition(*additionsIter);
144 }
145 m_additions.clear();
146 }
147
148 // Add in the defaults
149 auto subsystemIter = m_subsystems.begin();
150 for (; subsystemIter != m_subsystems.end(); subsystemIter++) {
151 Subsystem* lock = *subsystemIter;
152 if (lock->GetCurrentCommand() == nullptr) {
153 ProcessCommandAddition(lock->GetDefaultCommand());
154 }
155 lock->ConfirmCommand();
156 }
157
158 UpdateTable();
159}
160
161/**
162 * Registers a {@link Subsystem} to this {@link Scheduler}, so that the {@link
163 * Scheduler} might know if a default {@link Command} needs to be run.
164 *
165 * All {@link Subsystem Subsystems} should call this.
166 *
167 * @param system the system
168 */
169void Scheduler::RegisterSubsystem(Subsystem* subsystem) {
170 if (subsystem == nullptr) {
171 wpi_setWPIErrorWithContext(NullParameter, "subsystem");
172 return;
173 }
174 m_subsystems.insert(subsystem);
175}
176
177/**
178 * Removes the {@link Command} from the {@link Scheduler}.
179 *
180 * @param command the command to remove
181 */
182void Scheduler::Remove(Command* command) {
183 if (command == nullptr) {
184 wpi_setWPIErrorWithContext(NullParameter, "command");
185 return;
186 }
187
188 if (!m_commands.erase(command)) return;
189
190 Command::SubsystemSet requirements = command->GetRequirements();
191 auto iter = requirements.begin();
192 for (; iter != requirements.end(); iter++) {
193 Subsystem* lock = *iter;
194 lock->SetCurrentCommand(nullptr);
195 }
196
197 command->Removed();
198}
199
200void Scheduler::RemoveAll() {
201 while (m_commands.size() > 0) {
202 Remove(*m_commands.begin());
203 }
204}
205
206/**
207 * Completely resets the scheduler. Undefined behavior if running.
208 */
209void Scheduler::ResetAll() {
210 RemoveAll();
211 m_subsystems.clear();
212 m_buttons.clear();
213 m_additions.clear();
214 m_commands.clear();
215 m_table = nullptr;
216}
217
218/**
219 * Update the network tables associated with the Scheduler object on the
220 * SmartDashboard.
221 */
222void Scheduler::UpdateTable() {
223 CommandSet::iterator commandIter;
224 if (m_table != nullptr) {
225 // Get the list of possible commands to cancel
226 auto new_toCancel = m_table->GetValue("Cancel");
227 if (new_toCancel)
228 toCancel = new_toCancel->GetDoubleArray();
229 else
230 toCancel.resize(0);
231 // m_table->RetrieveValue("Ids", *ids);
232
233 // cancel commands that have had the cancel buttons pressed
234 // on the SmartDashboad
235 if (!toCancel.empty()) {
236 for (commandIter = m_commands.begin(); commandIter != m_commands.end();
237 ++commandIter) {
238 for (size_t i = 0; i < toCancel.size(); i++) {
239 Command* c = *commandIter;
240 if (c->GetID() == toCancel[i]) {
241 c->Cancel();
242 }
243 }
244 }
245 toCancel.resize(0);
246 m_table->PutValue("Cancel", nt::Value::MakeDoubleArray(toCancel));
247 }
248
249 // Set the running commands
250 if (m_runningCommandsChanged) {
251 commands.resize(0);
252 ids.resize(0);
253 for (commandIter = m_commands.begin(); commandIter != m_commands.end();
254 ++commandIter) {
255 Command* c = *commandIter;
256 commands.push_back(c->GetName());
257 ids.push_back(c->GetID());
258 }
259 m_table->PutValue("Names", nt::Value::MakeStringArray(commands));
260 m_table->PutValue("Ids", nt::Value::MakeDoubleArray(ids));
261 }
262 }
263}
264
265std::string Scheduler::GetName() const { return "Scheduler"; }
266
267std::string Scheduler::GetType() const { return "Scheduler"; }
268
269std::string Scheduler::GetSmartDashboardType() const { return "Scheduler"; }
270
271void Scheduler::InitTable(std::shared_ptr<ITable> subTable) {
272 m_table = subTable;
273
274 m_table->PutValue("Names", nt::Value::MakeStringArray(commands));
275 m_table->PutValue("Ids", nt::Value::MakeDoubleArray(ids));
276 m_table->PutValue("Cancel", nt::Value::MakeDoubleArray(toCancel));
277}
278
279std::shared_ptr<ITable> Scheduler::GetTable() const { return m_table; }