blob: 918a68f287db8203f425ee2a7f3cb1e43f1d7a3e [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +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 "NetworkTables/NetworkTable.h"
13#include "Synchronized.h"
14#include "WPIErrors.h"
15#include <iostream>
16#include <set>
17#include <semLib.h>
18
19Scheduler *Scheduler::_instance = NULL;
20
21Scheduler::Scheduler() :
22 m_tableLock(NULL),
23 m_table(NULL),
24 m_buttonsLock(NULL),
25 m_additionsLock(NULL),
26 m_adding(false)
27{
28 m_tableLock = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
29 m_buttonsLock = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
30 m_additionsLock = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
31
32 nUsageReporting::report(nUsageReporting::kResourceType_Command, nUsageReporting::kCommand_Scheduler);
33}
34
35Scheduler::~Scheduler()
36{
37 semTake(m_additionsLock, WAIT_FOREVER);
38 semDelete(m_additionsLock);
39
40 semTake(m_buttonsLock, WAIT_FOREVER);
41 semDelete(m_buttonsLock);
42
43 semTake(m_tableLock, WAIT_FOREVER);
44 semDelete(m_tableLock);
45}
46
47/**
48 * Returns the {@link Scheduler}, creating it if one does not exist.
49 * @return the {@link Scheduler}
50 */
51Scheduler *Scheduler::GetInstance()
52{
53 if (_instance == NULL)
54 _instance = new Scheduler();
55 return _instance;
56}
57
58void Scheduler::AddCommand(Command *command)
59{
60 Synchronized sync(m_additionsLock);
61 m_additions.push_back(command);
62}
63
64void Scheduler::AddButton(ButtonScheduler *button)
65{
66 Synchronized sync(m_buttonsLock);
67 m_buttons.push_back(button);
68}
69
70void Scheduler::ProcessCommandAddition(Command *command)
71{
72 if (command == NULL)
73 return;
74
75 // Check to make sure no adding during adding
76 if (m_adding)
77 {
78 wpi_setWPIErrorWithContext(IncompatibleState, "Can not start command from cancel method");
79 return;
80 }
81
82 // Only add if not already in
83 CommandSet::iterator found = m_commands.find(command);
84 if (found == m_commands.end())
85 {
86 // Check that the requirements can be had
87 Command::SubsystemSet requirements = command->GetRequirements();
88 Command::SubsystemSet::iterator iter;
89 for (iter = requirements.begin(); iter != requirements.end(); iter++)
90 {
91 Subsystem *lock = *iter;
92 if (lock->GetCurrentCommand() != NULL && !lock->GetCurrentCommand()->IsInterruptible())
93 return;
94 }
95
96 // Give it the requirements
97 m_adding = true;
98 for (iter = requirements.begin(); iter != requirements.end(); iter++)
99 {
100 Subsystem *lock = *iter;
101 if (lock->GetCurrentCommand() != NULL)
102 {
103 lock->GetCurrentCommand()->Cancel();
104 Remove(lock->GetCurrentCommand());
105 }
106 lock->SetCurrentCommand(command);
107 }
108 m_adding = false;
109
110 m_commands.insert(command);
111
112 command->StartRunning();
113 }
114}
115
116/**
117 * Runs a single iteration of the loop. This method should be called often in order to have a functioning
118 * {@link Command} system. The loop has five stages:
119 *
120 * <ol>
121 * <li> Poll the Buttons </li>
122 * <li> Execute/Remove the Commands </li>
123 * <li> Send values to SmartDashboard </li>
124 * <li> Add Commands </li>
125 * <li> Add Defaults </li>
126 * </ol>
127 */
128void Scheduler::Run()
129{
130 // Get button input (going backwards preserves button priority)
131 {
132 Synchronized sync(m_buttonsLock);
133 ButtonVector::reverse_iterator rButtonIter = m_buttons.rbegin();
134 for (; rButtonIter != m_buttons.rend(); rButtonIter++)
135 {
136 (*rButtonIter)->Execute();
137 }
138 }
139
140 // Loop through the commands
141 CommandSet::iterator commandIter = m_commands.begin();
142 for (; commandIter != m_commands.end();)
143 {
144 Command *command = *commandIter;
145 // Increment before potentially removing to keep the iterator valid
146 commandIter++;
147 if (!command->Run())
148 {
149 Remove(command);
150 }
151 }
152
153 // Send the value over the table
154 if (m_table != NULL) {
155 int count = 0;
156 Synchronized sync(m_tableLock);
157 m_table->BeginTransaction();
158 commandIter = m_commands.begin();
159 for (; commandIter != m_commands.end(); commandIter++)
160 {
161 char buf[10];
162 snprintf(buf, 10, "%d", ++count);
163 m_table->PutSubTable(buf, (*commandIter)->GetTable());
164 }
165 m_table->PutInt("count", count);
166 m_table->EndTransaction();
167 }
168
169 // Add the new things
170 {
171 Synchronized sync(m_additionsLock);
172 CommandVector::iterator additionsIter = m_additions.begin();
173 for (; additionsIter != m_additions.end(); additionsIter++)
174 {
175 ProcessCommandAddition(*additionsIter);
176 }
177 m_additions.clear();
178 }
179
180 // Add in the defaults
181 Command::SubsystemSet::iterator subsystemIter = m_subsystems.begin();
182 for (; subsystemIter != m_subsystems.end(); subsystemIter++)
183 {
184 Subsystem *lock = *subsystemIter;
185 if (lock->GetCurrentCommand() == NULL)
186 {
187 ProcessCommandAddition(lock->GetDefaultCommand());
188 }
189 lock->ConfirmCommand();
190 }
191}
192
193/**
194 * Registers a {@link Subsystem} to this {@link Scheduler}, so that the {@link Scheduler} might know
195 * if a default {@link Command} needs to be run. All {@link Subsystem Subsystems} should call this.
196 * @param system the system
197 */
198void Scheduler::RegisterSubsystem(Subsystem *subsystem)
199{
200 if (subsystem == NULL)
201 {
202 wpi_setWPIErrorWithContext(NullParameter, "subsystem");
203 return;
204 }
205 m_subsystems.insert(subsystem);
206}
207
208/**
209 * Removes the {@link Command} from the {@link Scheduler}.
210 * @param command the command to remove
211 */
212void Scheduler::Remove(Command *command)
213{
214 if (command == NULL)
215 {
216 wpi_setWPIErrorWithContext(NullParameter, "command");
217 return;
218 }
219
220 if (!m_commands.erase(command))
221 return;
222
223 Command::SubsystemSet requirements = command->GetRequirements();
224 Command::SubsystemSet::iterator iter = requirements.begin();
225 for (; iter != requirements.end(); iter++)
226 {
227 Subsystem *lock = *iter;
228 lock->SetCurrentCommand(NULL);
229 }
230
231 command->Removed();
232}
233
234std::string Scheduler::GetName()
235{
236 return "Scheduler";
237}
238
239std::string Scheduler::GetType()
240{
241 return "Scheduler";
242}
243
244NetworkTable *Scheduler::GetTable()
245{
246 if (m_table == NULL)
247 {
248 m_table = new NetworkTable();
249 m_table->PutInt("count", 0);
250 }
251 return m_table;
252}