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