blob: 1ee067a0b2e98b7431d366bdaf25621257ffb980 [file] [log] [blame]
jerrymf1579332013-02-07 01:56:28 +00001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2011. 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 $(WIND_BASE)/WPILib. */
5/*----------------------------------------------------------------------------*/
6
7#include "Commands/Scheduler.h"
8
9#include "Buttons/ButtonScheduler.h"
10#include "Commands/Subsystem.h"
11#include "NetworkCommunication/UsageReporting.h"
12#include "Synchronized.h"
13#include "WPIErrors.h"
14#include <iostream>
15#include <set>
16#include <semLib.h>
17#include <algorithm>
18
19Scheduler *Scheduler::_instance = NULL;
20
21Scheduler::Scheduler() :
jerrym42dedc02013-02-25 01:59:14 +000022 m_buttonsLock(NULL), m_additionsLock(NULL), m_adding(false) {
23 m_buttonsLock = semMCreate(
24 SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
25 m_additionsLock = semMCreate(
26 SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
jerrymf1579332013-02-07 01:56:28 +000027
jerrym42dedc02013-02-25 01:59:14 +000028 nUsageReporting::report(nUsageReporting::kResourceType_Command,
29 nUsageReporting::kCommand_Scheduler);
30
31 m_table = NULL;
jerrymf1579332013-02-07 01:56:28 +000032 m_enabled = true;
33}
34
jerrym42dedc02013-02-25 01:59:14 +000035Scheduler::~Scheduler() {
jerrymf1579332013-02-07 01:56:28 +000036 semTake(m_additionsLock, WAIT_FOREVER);
37 semDelete(m_additionsLock);
38
39 semTake(m_buttonsLock, WAIT_FOREVER);
40 semDelete(m_buttonsLock);
41}
42
43/**
44 * Returns the {@link Scheduler}, creating it if one does not exist.
45 * @return the {@link Scheduler}
46 */
jerrym42dedc02013-02-25 01:59:14 +000047Scheduler *Scheduler::GetInstance() {
jerrymf1579332013-02-07 01:56:28 +000048 if (_instance == NULL)
49 _instance = new Scheduler();
50 return _instance;
51}
52
53void Scheduler::SetEnabled(bool enabled) {
54 m_enabled = enabled;
55}
56
57/**
58 * Add a command to be scheduled later.
59 * In any pass through the scheduler, all commands are added to the additions list, then
60 * at the end of the pass, they are all scheduled.
61 * @param command The command to be scheduled
62 */
jerrym42dedc02013-02-25 01:59:14 +000063void Scheduler::AddCommand(Command *command) {
jerrymf1579332013-02-07 01:56:28 +000064 Synchronized sync(m_additionsLock);
jerrym42dedc02013-02-25 01:59:14 +000065 if (std::find(m_additions.begin(), m_additions.end(), command)
66 != m_additions.end())
jerrymf1579332013-02-07 01:56:28 +000067 return;
68 m_additions.push_back(command);
69}
70
jerrym42dedc02013-02-25 01:59:14 +000071void Scheduler::AddButton(ButtonScheduler *button) {
jerrymf1579332013-02-07 01:56:28 +000072 Synchronized sync(m_buttonsLock);
73 m_buttons.push_back(button);
74}
75
jerrym42dedc02013-02-25 01:59:14 +000076void Scheduler::ProcessCommandAddition(Command *command) {
jerrymf1579332013-02-07 01:56:28 +000077 if (command == NULL)
78 return;
79
80 // Check to make sure no adding during adding
jerrym42dedc02013-02-25 01:59:14 +000081 if (m_adding) {
jerrymf1579332013-02-07 01:56:28 +000082 wpi_setWPIErrorWithContext(IncompatibleState, "Can not start command from cancel method");
83 return;
84 }
85
86 // Only add if not already in
87 CommandSet::iterator found = m_commands.find(command);
jerrym42dedc02013-02-25 01:59:14 +000088 if (found == m_commands.end()) {
jerrymf1579332013-02-07 01:56:28 +000089 // Check that the requirements can be had
90 Command::SubsystemSet requirements = command->GetRequirements();
91 Command::SubsystemSet::iterator iter;
jerrym42dedc02013-02-25 01:59:14 +000092 for (iter = requirements.begin(); iter != requirements.end(); iter++) {
jerrymf1579332013-02-07 01:56:28 +000093 Subsystem *lock = *iter;
jerrym42dedc02013-02-25 01:59:14 +000094 if (lock->GetCurrentCommand() != NULL
95 && !lock->GetCurrentCommand()->IsInterruptible())
jerrymf1579332013-02-07 01:56:28 +000096 return;
97 }
98
99 // Give it the requirements
100 m_adding = true;
jerrym42dedc02013-02-25 01:59:14 +0000101 for (iter = requirements.begin(); iter != requirements.end(); iter++) {
jerrymf1579332013-02-07 01:56:28 +0000102 Subsystem *lock = *iter;
jerrym42dedc02013-02-25 01:59:14 +0000103 if (lock->GetCurrentCommand() != NULL) {
jerrymf1579332013-02-07 01:56:28 +0000104 lock->GetCurrentCommand()->Cancel();
105 Remove(lock->GetCurrentCommand());
106 }
107 lock->SetCurrentCommand(command);
108 }
109 m_adding = false;
110
111 m_commands.insert(command);
112
113 command->StartRunning();
jerrym42dedc02013-02-25 01:59:14 +0000114 m_runningCommandsChanged = true;
jerrymf1579332013-02-07 01:56:28 +0000115 }
116}
117
118/**
119 * Runs a single iteration of the loop. This method should be called often in order to have a functioning
120 * {@link Command} system. The loop has five stages:
121 *
122 * <ol>
123 * <li> Poll the Buttons </li>
124 * <li> Execute/Remove the Commands </li>
125 * <li> Send values to SmartDashboard </li>
126 * <li> Add Commands </li>
127 * <li> Add Defaults </li>
128 * </ol>
129 */
jerrym42dedc02013-02-25 01:59:14 +0000130void Scheduler::Run() {
jerrymf1579332013-02-07 01:56:28 +0000131 // Get button input (going backwards preserves button priority)
132 {
jerrym42dedc02013-02-25 01:59:14 +0000133 if (!m_enabled)
134 return;
jerrymf1579332013-02-07 01:56:28 +0000135
136 Synchronized sync(m_buttonsLock);
137 ButtonVector::reverse_iterator rButtonIter = m_buttons.rbegin();
jerrym42dedc02013-02-25 01:59:14 +0000138 for (; rButtonIter != m_buttons.rend(); rButtonIter++) {
jerrymf1579332013-02-07 01:56:28 +0000139 (*rButtonIter)->Execute();
140 }
141 }
jerrym42dedc02013-02-25 01:59:14 +0000142
143 m_runningCommandsChanged = false;
jerrymf1579332013-02-07 01:56:28 +0000144
145 // Loop through the commands
146 CommandSet::iterator commandIter = m_commands.begin();
jerrym42dedc02013-02-25 01:59:14 +0000147 for (; commandIter != m_commands.end();) {
jerrymf1579332013-02-07 01:56:28 +0000148 Command *command = *commandIter;
149 // Increment before potentially removing to keep the iterator valid
150 commandIter++;
jerrym42dedc02013-02-25 01:59:14 +0000151 if (!command->Run()) {
jerrymf1579332013-02-07 01:56:28 +0000152 Remove(command);
jerrym42dedc02013-02-25 01:59:14 +0000153 m_runningCommandsChanged = true;
jerrymf1579332013-02-07 01:56:28 +0000154 }
155 }
156
157 // Add the new things
158 {
159 Synchronized sync(m_additionsLock);
160 CommandVector::iterator additionsIter = m_additions.begin();
jerrym42dedc02013-02-25 01:59:14 +0000161 for (; additionsIter != m_additions.end(); additionsIter++) {
jerrymf1579332013-02-07 01:56:28 +0000162 ProcessCommandAddition(*additionsIter);
163 }
164 m_additions.clear();
165 }
166
167 // Add in the defaults
168 Command::SubsystemSet::iterator subsystemIter = m_subsystems.begin();
jerrym42dedc02013-02-25 01:59:14 +0000169 for (; subsystemIter != m_subsystems.end(); subsystemIter++) {
jerrymf1579332013-02-07 01:56:28 +0000170 Subsystem *lock = *subsystemIter;
jerrym42dedc02013-02-25 01:59:14 +0000171 if (lock->GetCurrentCommand() == NULL) {
jerrymf1579332013-02-07 01:56:28 +0000172 ProcessCommandAddition(lock->GetDefaultCommand());
173 }
174 lock->ConfirmCommand();
175 }
jerrym42dedc02013-02-25 01:59:14 +0000176
jerrymf1579332013-02-07 01:56:28 +0000177 UpdateTable();
178}
179
180/**
181 * Registers a {@link Subsystem} to this {@link Scheduler}, so that the {@link Scheduler} might know
182 * if a default {@link Command} needs to be run. All {@link Subsystem Subsystems} should call this.
183 * @param system the system
184 */
jerrym42dedc02013-02-25 01:59:14 +0000185void Scheduler::RegisterSubsystem(Subsystem *subsystem) {
186 if (subsystem == NULL) {
jerrymf1579332013-02-07 01:56:28 +0000187 wpi_setWPIErrorWithContext(NullParameter, "subsystem");
188 return;
189 }
190 m_subsystems.insert(subsystem);
191}
192
193/**
194 * Removes the {@link Command} from the {@link Scheduler}.
195 * @param command the command to remove
196 */
jerrym42dedc02013-02-25 01:59:14 +0000197void Scheduler::Remove(Command *command) {
198 if (command == NULL) {
jerrymf1579332013-02-07 01:56:28 +0000199 wpi_setWPIErrorWithContext(NullParameter, "command");
200 return;
201 }
202
203 if (!m_commands.erase(command))
204 return;
205
206 Command::SubsystemSet requirements = command->GetRequirements();
207 Command::SubsystemSet::iterator iter = requirements.begin();
jerrym42dedc02013-02-25 01:59:14 +0000208 for (; iter != requirements.end(); iter++) {
jerrymf1579332013-02-07 01:56:28 +0000209 Subsystem *lock = *iter;
210 lock->SetCurrentCommand(NULL);
211 }
212
213 command->Removed();
214}
215
216void Scheduler::RemoveAll() {
jerrym42dedc02013-02-25 01:59:14 +0000217 while (m_commands.size() > 0) {
jerrymf1579332013-02-07 01:56:28 +0000218 Remove(*m_commands.begin());
219 }
220}
221
jerrym42dedc02013-02-25 01:59:14 +0000222/**
223 * Update the network tables associated with the Scheduler object on the SmartDashboard
224 */
jerrymf1579332013-02-07 01:56:28 +0000225void Scheduler::UpdateTable() {
226 CommandSet::iterator commandIter;
227 if (m_table != NULL) {
jerrym42dedc02013-02-25 01:59:14 +0000228 // Get the list of possible commands to cancel
jerrymf1579332013-02-07 01:56:28 +0000229 m_table->RetrieveValue("Cancel", *toCancel);
jerrym42dedc02013-02-25 01:59:14 +0000230// m_table->RetrieveValue("Ids", *ids);
231
232 // cancel commands that have had the cancel buttons pressed
233 // on the SmartDashboad
234 if (toCancel->size() > 0) {
235 for (commandIter = m_commands.begin(); commandIter
236 != m_commands.end(); ++commandIter) {
237 for (unsigned i = 0; i < toCancel->size(); i++) {
238 Command *c = *commandIter;
239 if (c->GetID() == toCancel->get(i)) {
240 c->Cancel();
241 }
jerrymf1579332013-02-07 01:56:28 +0000242 }
243 }
jerrym42dedc02013-02-25 01:59:14 +0000244 toCancel->setSize(0);
245 m_table->PutValue("Cancel", *toCancel);
jerrymf1579332013-02-07 01:56:28 +0000246 }
jerrymf1579332013-02-07 01:56:28 +0000247
248 // Set the running commands
jerrym42dedc02013-02-25 01:59:14 +0000249 if (m_runningCommandsChanged) {
250 commands->setSize(0);
251 ids->setSize(0);
252 for (commandIter = m_commands.begin(); commandIter != m_commands.end(); ++commandIter) {
253 Command *c = *commandIter;
254 commands->add(c->GetName());
255 ids->add(c->GetID());
256 }
257 m_table->PutValue("Names", *commands);
258 m_table->PutValue("Ids", *ids);
jerrymf1579332013-02-07 01:56:28 +0000259 }
jerrymf1579332013-02-07 01:56:28 +0000260 }
261}
262
263std::string Scheduler::GetName() {
264 return "Scheduler";
265}
266
267std::string Scheduler::GetType() {
268 return "Scheduler";
269}
270
271std::string Scheduler::GetSmartDashboardType() {
272 return "Scheduler";
273}
274
275void Scheduler::InitTable(ITable *subTable) {
276 m_table = subTable;
277 commands = new StringArray();
278 ids = new NumberArray();
279 toCancel = new NumberArray();
jerrym42dedc02013-02-25 01:59:14 +0000280
jerrymf1579332013-02-07 01:56:28 +0000281 m_table->PutValue("Names", *commands);
282 m_table->PutValue("Ids", *ids);
283 m_table->PutValue("Cancel", *toCancel);
284}
285
286ITable * Scheduler::GetTable() {
287 return m_table;
288}