blob: 9cd67d6e1e94e93467d5da5c6cf09c56dcb62d6b [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() :
22 m_buttonsLock(NULL),
23 m_additionsLock(NULL),
24 m_adding(false)
25{
26 m_buttonsLock = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
27 m_additionsLock = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
28
29 nUsageReporting::report(nUsageReporting::kResourceType_Command, nUsageReporting::kCommand_Scheduler);
30
31 m_enabled = true;
32}
33
34Scheduler::~Scheduler()
35{
36 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 */
47Scheduler *Scheduler::GetInstance()
48{
49 if (_instance == NULL)
50 _instance = new Scheduler();
51 return _instance;
52}
53
54void Scheduler::SetEnabled(bool enabled) {
55 m_enabled = enabled;
56}
57
58/**
59 * Add a command to be scheduled later.
60 * In any pass through the scheduler, all commands are added to the additions list, then
61 * at the end of the pass, they are all scheduled.
62 * @param command The command to be scheduled
63 */
64void Scheduler::AddCommand(Command *command)
65{
66 Synchronized sync(m_additionsLock);
67 if (std::find(m_additions.begin(), m_additions.end(), command) != m_additions.end())
68 return;
69 m_additions.push_back(command);
70}
71
72void Scheduler::AddButton(ButtonScheduler *button)
73{
74 Synchronized sync(m_buttonsLock);
75 m_buttons.push_back(button);
76}
77
78void Scheduler::ProcessCommandAddition(Command *command)
79{
80 if (command == NULL)
81 return;
82
83 // Check to make sure no adding during adding
84 if (m_adding)
85 {
86 wpi_setWPIErrorWithContext(IncompatibleState, "Can not start command from cancel method");
87 return;
88 }
89
90 // Only add if not already in
91 CommandSet::iterator found = m_commands.find(command);
92 if (found == m_commands.end())
93 {
94 // Check that the requirements can be had
95 Command::SubsystemSet requirements = command->GetRequirements();
96 Command::SubsystemSet::iterator iter;
97 for (iter = requirements.begin(); iter != requirements.end(); iter++)
98 {
99 Subsystem *lock = *iter;
100 if (lock->GetCurrentCommand() != NULL && !lock->GetCurrentCommand()->IsInterruptible())
101 return;
102 }
103
104 // Give it the requirements
105 m_adding = true;
106 for (iter = requirements.begin(); iter != requirements.end(); iter++)
107 {
108 Subsystem *lock = *iter;
109 if (lock->GetCurrentCommand() != NULL)
110 {
111 lock->GetCurrentCommand()->Cancel();
112 Remove(lock->GetCurrentCommand());
113 }
114 lock->SetCurrentCommand(command);
115 }
116 m_adding = false;
117
118 m_commands.insert(command);
119
120 command->StartRunning();
121 }
122}
123
124/**
125 * Runs a single iteration of the loop. This method should be called often in order to have a functioning
126 * {@link Command} system. The loop has five stages:
127 *
128 * <ol>
129 * <li> Poll the Buttons </li>
130 * <li> Execute/Remove the Commands </li>
131 * <li> Send values to SmartDashboard </li>
132 * <li> Add Commands </li>
133 * <li> Add Defaults </li>
134 * </ol>
135 */
136void Scheduler::Run()
137{
138 // Get button input (going backwards preserves button priority)
139 {
140 if (!m_enabled) return;
141
142 Synchronized sync(m_buttonsLock);
143 ButtonVector::reverse_iterator rButtonIter = m_buttons.rbegin();
144 for (; rButtonIter != m_buttons.rend(); rButtonIter++)
145 {
146 (*rButtonIter)->Execute();
147 }
148 }
149
150 // Loop through the commands
151 CommandSet::iterator commandIter = m_commands.begin();
152 for (; commandIter != m_commands.end();)
153 {
154 Command *command = *commandIter;
155 // Increment before potentially removing to keep the iterator valid
156 commandIter++;
157 if (!command->Run())
158 {
159 Remove(command);
160 }
161 }
162
163 // Add the new things
164 {
165 Synchronized sync(m_additionsLock);
166 CommandVector::iterator additionsIter = m_additions.begin();
167 for (; additionsIter != m_additions.end(); additionsIter++)
168 {
169 ProcessCommandAddition(*additionsIter);
170 }
171 m_additions.clear();
172 }
173
174 // Add in the defaults
175 Command::SubsystemSet::iterator subsystemIter = m_subsystems.begin();
176 for (; subsystemIter != m_subsystems.end(); subsystemIter++)
177 {
178 Subsystem *lock = *subsystemIter;
179 if (lock->GetCurrentCommand() == NULL)
180 {
181 ProcessCommandAddition(lock->GetDefaultCommand());
182 }
183 lock->ConfirmCommand();
184 }
185
186 UpdateTable();
187}
188
189/**
190 * Registers a {@link Subsystem} to this {@link Scheduler}, so that the {@link Scheduler} might know
191 * if a default {@link Command} needs to be run. All {@link Subsystem Subsystems} should call this.
192 * @param system the system
193 */
194void Scheduler::RegisterSubsystem(Subsystem *subsystem)
195{
196 if (subsystem == NULL)
197 {
198 wpi_setWPIErrorWithContext(NullParameter, "subsystem");
199 return;
200 }
201 m_subsystems.insert(subsystem);
202}
203
204/**
205 * Removes the {@link Command} from the {@link Scheduler}.
206 * @param command the command to remove
207 */
208void Scheduler::Remove(Command *command)
209{
210 if (command == NULL)
211 {
212 wpi_setWPIErrorWithContext(NullParameter, "command");
213 return;
214 }
215
216 if (!m_commands.erase(command))
217 return;
218
219 Command::SubsystemSet requirements = command->GetRequirements();
220 Command::SubsystemSet::iterator iter = requirements.begin();
221 for (; iter != requirements.end(); iter++)
222 {
223 Subsystem *lock = *iter;
224 lock->SetCurrentCommand(NULL);
225 }
226
227 command->Removed();
228}
229
230void Scheduler::RemoveAll() {
231 while(m_commands.size()>0){
232 Remove(*m_commands.begin());
233 }
234}
235
236void Scheduler::UpdateTable() {
237 CommandSet::iterator commandIter;
238 if (m_table != NULL) {
239 // Get the commands to cancel
240 m_table->RetrieveValue("Cancel", *toCancel);
241
242 for (commandIter = m_commands.begin(); commandIter != m_commands.end();) {
243 for (unsigned i = 0; i < toCancel->size(); i++) {
244 Command *c = *commandIter;
245 if ((unsigned)c == toCancel->get(i)) {
246 c->Cancel();
247 }
248 }
249 }
250 toCancel->setSize(0);
251 m_table->PutValue("Cancel", *toCancel);
252
253 commands->setSize(0);
254 ids->setSize(0);
255
256 // Set the running commands
257 for (commandIter = m_commands.begin(); commandIter != m_commands.end();) {
258 Command *c = *commandIter;
259 commands->add(c->GetName());
260 ids->add((unsigned)c);
261 }
262 m_table->PutValue("Names", *commands);
263 m_table->PutValue("Ids", *ids);
264 }
265}
266
267std::string Scheduler::GetName() {
268 return "Scheduler";
269}
270
271std::string Scheduler::GetType() {
272 return "Scheduler";
273}
274
275std::string Scheduler::GetSmartDashboardType() {
276 return "Scheduler";
277}
278
279void Scheduler::InitTable(ITable *subTable) {
280 m_table = subTable;
281 commands = new StringArray();
282 ids = new NumberArray();
283 toCancel = new NumberArray();
284
285 m_table->PutValue("Names", *commands);
286 m_table->PutValue("Ids", *ids);
287 m_table->PutValue("Cancel", *toCancel);
288}
289
290ITable * Scheduler::GetTable() {
291 return m_table;
292}