blob: 86a456b189997b523acce563471f5181ec192725 [file] [log] [blame]
Austin Schuh010eb812014-10-25 18:06:49 -07001#include <stdio.h>
2#include <string.h>
Austin Schuh010eb812014-10-25 18:06:49 -07003#include <unistd.h>
4#include <inttypes.h>
5
Brian Silvermand8f403a2014-12-13 19:12:04 -05006#include <thread>
7#include <mutex>
8#include <functional>
9
Austin Schuh010eb812014-10-25 18:06:49 -070010#include "aos/common/controls/output_check.q.h"
11#include "aos/common/controls/sensor_generation.q.h"
12#include "aos/common/logging/logging.h"
13#include "aos/common/logging/queue_logging.h"
Brian Silvermand8f403a2014-12-13 19:12:04 -050014#include "aos/common/messages/robot_state.q.h"
Austin Schuh010eb812014-10-25 18:06:49 -070015#include "aos/common/time.h"
16#include "aos/common/util/log_interval.h"
17#include "aos/common/util/phased_loop.h"
18#include "aos/common/util/wrapping_counter.h"
Austin Schuh010eb812014-10-25 18:06:49 -070019#include "aos/linux_code/init.h"
20
21#include "frc971/control_loops/drivetrain/drivetrain.q.h"
Austin Schuh010eb812014-10-25 18:06:49 -070022#include "frc971/constants.h"
Daniel Pettiaece37f2014-10-25 17:13:44 -070023#include "frc971/shifter_hall_effect.h"
Austin Schuh010eb812014-10-25 18:06:49 -070024
Brian Silvermanda45b6c2014-12-28 11:36:50 -080025#include "frc971/wpilib/hall_effect.h"
26#include "frc971/wpilib/joystick_sender.h"
Brian Silvermand8f403a2014-12-13 19:12:04 -050027#include "frc971/wpilib/loop_output_handler.h"
28#include "frc971/wpilib/buffered_solenoid.h"
29#include "frc971/wpilib/buffered_pcm.h"
Brian Silverman07ec88e2014-12-28 00:13:08 -080030#include "frc971/wpilib/gyro_sender.h"
Brian Silvermanda45b6c2014-12-28 11:36:50 -080031
Brian Silvermancb77f232014-12-19 21:48:36 -080032#include "Encoder.h"
Brian Silvermancb77f232014-12-19 21:48:36 -080033#include "Talon.h"
34#include "DriverStation.h"
35#include "AnalogInput.h"
Brian Silvermancb77f232014-12-19 21:48:36 -080036#include "Compressor.h"
37#include "RobotBase.h"
Austin Schuh010eb812014-10-25 18:06:49 -070038
39#ifndef M_PI
40#define M_PI 3.14159265358979323846
41#endif
42
43using ::aos::util::SimpleLogInterval;
44using ::frc971::control_loops::drivetrain;
Austin Schuh010eb812014-10-25 18:06:49 -070045using ::aos::util::WrappingCounter;
46
47namespace frc971 {
Brian Silvermanda45b6c2014-12-28 11:36:50 -080048namespace wpilib {
Austin Schuh010eb812014-10-25 18:06:49 -070049
50class priority_mutex {
51 public:
52 typedef pthread_mutex_t *native_handle_type;
53
54 // TODO(austin): Write a test case for the mutex, and make the constructor
55 // constexpr.
56 priority_mutex() {
57 pthread_mutexattr_t attr;
Austin Schuh010eb812014-10-25 18:06:49 -070058#ifdef NDEBUG
Brian Silvermanda45b6c2014-12-28 11:36:50 -080059#error "Won't let assert_perror be no-op ed"
Austin Schuh010eb812014-10-25 18:06:49 -070060#endif
Austin Schuhdb516032014-12-28 00:12:38 -080061 // Turn on priority inheritance.
Austin Schuh010eb812014-10-25 18:06:49 -070062 assert_perror(pthread_mutexattr_init(&attr));
63 assert_perror(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL));
64 assert_perror(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT));
65
66 assert_perror(pthread_mutex_init(native_handle(), &attr));
67
68 assert_perror(pthread_mutexattr_destroy(&attr));
69 }
70
71 ~priority_mutex() { pthread_mutex_destroy(&handle_); }
72
73 void lock() { assert_perror(pthread_mutex_lock(&handle_)); }
74 bool try_lock() {
75 int ret = pthread_mutex_trylock(&handle_);
76 if (ret == 0) {
77 return true;
78 } else if (ret == EBUSY) {
79 return false;
80 } else {
81 assert_perror(ret);
82 }
83 }
84 void unlock() { assert_perror(pthread_mutex_unlock(&handle_)); }
85
86 native_handle_type native_handle() { return &handle_; }
87
88 private:
89 DISALLOW_COPY_AND_ASSIGN(priority_mutex);
90 pthread_mutex_t handle_;
91};
92
Brian Silvermanda45b6c2014-12-28 11:36:50 -080093// TODO(brian): Split this out into a separate file once DMA is in.
Austin Schuh010eb812014-10-25 18:06:49 -070094class EdgeCounter {
95 public:
96 EdgeCounter(int priority, Encoder *encoder, HallEffect *input,
97 priority_mutex *mutex)
98 : priority_(priority),
99 encoder_(encoder),
100 input_(input),
101 mutex_(mutex),
102 run_(true),
103 any_interrupt_count_(0) {
104 thread_.reset(new ::std::thread(::std::ref(*this)));
105 }
106
107 // Waits for interrupts, locks the mutex, and updates the internal state.
108 // Updates the any_interrupt_count count when the interrupt comes in without
109 // the lock.
Austin Schuhdb516032014-12-28 00:12:38 -0800110 void operator()() {
Brian Silverman2fe007c2014-12-28 12:20:01 -0800111 ::aos::SetCurrentThreadName("EdgeCounter_" +
112 ::std::to_string(input_->GetChannel()));
Austin Schuh010eb812014-10-25 18:06:49 -0700113
114 input_->RequestInterrupts();
115 input_->SetUpSourceEdge(true, true);
116
117 {
118 ::std::unique_lock<priority_mutex> mutex_guard(*mutex_);
119 current_value_ = input_->GetHall();
120 }
121
Brian Silverman2fe007c2014-12-28 12:20:01 -0800122 ::aos::SetCurrentThreadRealtimePriority(priority_);
Austin Schuh010eb812014-10-25 18:06:49 -0700123 InterruptableSensorBase::WaitResult result = InterruptableSensorBase::kBoth;
124 while (run_) {
125 result = input_->WaitForInterrupt(
126 0.1, result != InterruptableSensorBase::kTimeout);
127 if (result == InterruptableSensorBase::kTimeout) {
128 continue;
129 }
130 ++any_interrupt_count_;
131
132 ::std::unique_lock<priority_mutex> mutex_guard(*mutex_);
133 int32_t encoder_value = encoder_->GetRaw();
134 bool hall_value = input_->GetHall();
135 if (current_value_ != hall_value) {
136 if (hall_value) {
137 ++positive_interrupt_count_;
138 last_positive_encoder_value_ = encoder_value;
139 } else {
140 ++negative_interrupt_count_;
141 last_negative_encoder_value_ = encoder_value;
142 }
143 } else {
144 LOG(WARNING, "Detected spurious edge on %d. Dropping it.\n",
145 input_->GetChannel());
146 }
147
148 current_value_ = hall_value;
149 }
150 }
151
152 // Updates the internal hall effect value given this new observation.
153 // The mutex provided at construction time must be held during this operation.
154 void set_polled_value(bool value) {
155 polled_value_ = value;
156 bool miss_match = (value != current_value_);
157 if (miss_match && last_miss_match_) {
158 current_value_ = value;
159 last_miss_match_ = false;
160 } else {
161 last_miss_match_ = miss_match;
162 }
163 }
164
165 // Signals the thread to quit next time it gets an interrupt.
166 void Quit() {
167 run_ = false;
168 thread_->join();
169 }
170
171 // Returns the total number of interrupts since construction time. This
172 // should be done without the mutex held.
173 int any_interrupt_count() const { return any_interrupt_count_; }
174 // Returns the current interrupt edge counts and encoder values.
175 // The mutex provided at construction time must be held during this operation.
176 int positive_interrupt_count() const { return positive_interrupt_count_; }
177 int negative_interrupt_count() const { return negative_interrupt_count_; }
178 int32_t last_positive_encoder_value() const {
179 return last_positive_encoder_value_;
180 }
181 int32_t last_negative_encoder_value() const {
182 return last_negative_encoder_value_;
183 }
184 // Returns the current polled value.
185 bool polled_value() const { return polled_value_; }
186
187 private:
188 int priority_;
189 Encoder *encoder_;
190 HallEffect *input_;
191 priority_mutex *mutex_;
192 ::std::atomic<bool> run_;
193
194 ::std::atomic<int> any_interrupt_count_;
195
196 // The following variables represent the current state. They must be
197 // synchronized by mutex_;
198 bool current_value_ = false;
199 bool polled_value_ = false;
200 bool last_miss_match_ = true;
201 int positive_interrupt_count_ = 0;
202 int negative_interrupt_count_ = 0;
203 int32_t last_positive_encoder_value_ = 0;
204 int32_t last_negative_encoder_value_ = 0;
205
206 ::std::unique_ptr<::std::thread> thread_;
207};
208
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800209// This class will synchronize sampling edges on a bunch of HallEffects with
Austin Schuh010eb812014-10-25 18:06:49 -0700210// the periodic poll.
211//
212// The data is provided to subclasses by calling SaveState when the state is
213// consistent and ready.
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800214//
215// TODO(brian): Split this out into a separate file once DMA is in.
Austin Schuh010eb812014-10-25 18:06:49 -0700216template <int num_sensors>
217class PeriodicHallSynchronizer {
218 public:
219 PeriodicHallSynchronizer(
220 const char *name, int priority, int interrupt_priority,
221 ::std::unique_ptr<Encoder> encoder,
222 ::std::array<::std::unique_ptr<HallEffect>, num_sensors> *sensors)
223 : name_(name),
224 priority_(priority),
225 encoder_(::std::move(encoder)),
226 run_(true) {
227 for (int i = 0; i < num_sensors; ++i) {
228 sensors_[i] = ::std::move((*sensors)[i]);
229 edge_counters_[i] = ::std::unique_ptr<EdgeCounter>(new EdgeCounter(
230 interrupt_priority, encoder_.get(), sensors_[i].get(), &mutex_));
231 }
232 }
233
234 const char *name() const { return name_.c_str(); }
235
236 void StartThread() { thread_.reset(new ::std::thread(::std::ref(*this))); }
237
238 // Called when the state is consistent and up to date.
239 virtual void SaveState() = 0;
240
241 // Starts a sampling iteration. See RunIteration for usage.
242 void StartIteration() {
243 // Start by capturing the current interrupt counts.
244 for (int i = 0; i < num_sensors; ++i) {
245 interrupt_counts_[i] = edge_counters_[i]->any_interrupt_count();
246 }
247
248 {
249 // Now, update the encoder and sensor values.
250 ::std::unique_lock<priority_mutex> mutex_guard(mutex_);
251 encoder_value_ = encoder_->GetRaw();
252 for (int i = 0; i < num_sensors; ++i) {
253 edge_counters_[i]->set_polled_value(sensors_[i]->GetHall());
254 }
255 }
256 }
257
258 // Attempts to finish a sampling iteration. See RunIteration for usage.
259 // Returns true if the iteration succeeded, and false otherwise.
260 bool TryFinishingIteration() {
261 // Make sure no interrupts have occurred while we were waiting. If they
262 // have, we are in an inconsistent state and need to try again.
263 ::std::unique_lock<priority_mutex> mutex_guard(mutex_);
264 bool retry = false;
265 for (int i = 0; i < num_sensors; ++i) {
266 retry = retry || (interrupt_counts_[i] !=
267 edge_counters_[i]->any_interrupt_count());
268 }
269 if (!retry) {
270 SaveState();
271 return true;
272 }
273 LOG(WARNING, "Got an interrupt while sampling encoder %s, retrying\n",
274 name());
275 return false;
276 }
277
278 void RunIteration() {
279 while (true) {
280 StartIteration();
281
282 // Wait more than the amount of time it takes for a digital input change
283 // to go from visible to software to having triggered an interrupt.
284 ::aos::time::SleepFor(::aos::time::Time::InUS(120));
285
286 if (TryFinishingIteration()) {
287 return;
288 }
289 }
290 }
291
292 void operator()() {
Brian Silverman2fe007c2014-12-28 12:20:01 -0800293 ::aos::SetCurrentThreadName("HallSync" + ::std::to_string(num_sensors));
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800294 ::aos::SetCurrentThreadRealtimePriority(priority_);
Austin Schuh010eb812014-10-25 18:06:49 -0700295 while (run_) {
296 ::aos::time::PhasedLoopXMS(10, 9000);
297 RunIteration();
298 }
299 }
300
301 void Quit() {
302 run_ = false;
303 for (int i = 0; i < num_sensors; ++i) {
304 edge_counters_[i]->Quit();
305 }
306 if (thread_) {
307 thread_->join();
308 }
309 }
310
311 protected:
312 // These values are only safe to fetch from inside SaveState()
313 int32_t encoder_value() const { return encoder_value_; }
314 ::std::array<::std::unique_ptr<EdgeCounter>, num_sensors> &edge_counters() {
315 return edge_counters_;
316 }
317
318 private:
319 // A descriptive name for error messages.
320 ::std::string name_;
321 // The priority of the polling thread.
322 int priority_;
323 // The Encoder to sample.
324 ::std::unique_ptr<Encoder> encoder_;
325 // A list of all the digital inputs.
326 ::std::array<::std::unique_ptr<HallEffect>, num_sensors> sensors_;
327 // The mutex used to synchronize all the state.
328 priority_mutex mutex_;
329 ::std::atomic<bool> run_;
330
331 // The state.
332 // The current encoder value.
333 int32_t encoder_value_ = 0;
334 // The current edge counters.
335 ::std::array<::std::unique_ptr<EdgeCounter>, num_sensors> edge_counters_;
336
337 ::std::unique_ptr<::std::thread> thread_;
338 ::std::array<int, num_sensors> interrupt_counts_;
339};
340
341double drivetrain_translate(int32_t in) {
Austin Schuhdb516032014-12-28 00:12:38 -0800342 return static_cast<double>(in) /
343 (256.0 /*cpr*/ * 2.0 /*2x. Stupid WPILib*/) *
344 (18.0 / 50.0 /*output stage*/) * (56.0 / 30.0 /*encoder gears*/)
345 // * constants::GetValues().drivetrain_encoder_ratio
346 *
347 (3.5 /*wheel diameter*/ * 2.54 / 100.0 * M_PI);
Austin Schuh010eb812014-10-25 18:06:49 -0700348}
349
Austin Schuh010eb812014-10-25 18:06:49 -0700350class SensorReader {
351 public:
352 SensorReader()
Brian Silverman20141f92015-01-05 17:39:01 -0800353 : left_encoder_(new Encoder(11, 10, false, Encoder::k2X)), // E0
Brian Silvermancb77f232014-12-19 21:48:36 -0800354 right_encoder_(new Encoder(13, 12, false, Encoder::k2X)), // E1
Austin Schuh010eb812014-10-25 18:06:49 -0700355 run_(true) {
356 filter_.SetPeriodNanoSeconds(100000);
Austin Schuh010eb812014-10-25 18:06:49 -0700357 }
358
359 void operator()() {
Brian Silverman2fe007c2014-12-28 12:20:01 -0800360 ::aos::SetCurrentThreadName("SensorReader");
361
Austin Schuh010eb812014-10-25 18:06:49 -0700362 const int kPriority = 30;
Brian Silverman20141f92015-01-05 17:39:01 -0800363 //const int kInterruptPriority = 55;
Austin Schuh010eb812014-10-25 18:06:49 -0700364
Brian Silverman2fe007c2014-12-28 12:20:01 -0800365 ::aos::SetCurrentThreadRealtimePriority(kPriority);
Austin Schuh010eb812014-10-25 18:06:49 -0700366 while (run_) {
Brian Silverman20141f92015-01-05 17:39:01 -0800367 ::aos::time::PhasedLoopXMS(5, 9000);
Austin Schuh010eb812014-10-25 18:06:49 -0700368 RunIteration();
Austin Schuh010eb812014-10-25 18:06:49 -0700369 }
Austin Schuh010eb812014-10-25 18:06:49 -0700370 }
371
372 void RunIteration() {
Austin Schuh010eb812014-10-25 18:06:49 -0700373 DriverStation *ds = DriverStation::GetInstance();
374
Austin Schuhdb516032014-12-28 00:12:38 -0800375 if (ds->IsSysActive()) {
Austin Schuh010eb812014-10-25 18:06:49 -0700376 auto message = ::aos::controls::output_check_received.MakeMessage();
377 // TODO(brians): Actually read a pulse value from the roboRIO.
378 message->pwm_value = 0;
379 message->pulse_length = -1;
380 LOG_STRUCT(DEBUG, "received", *message);
381 message.Send();
382 }
383
Austin Schuh010eb812014-10-25 18:06:49 -0700384 // TODO(austin): Calibrate the shifter constants again.
Brian Silverman20141f92015-01-05 17:39:01 -0800385 // TODO(sensors): Hook up the new dog position sensors.
Austin Schuh010eb812014-10-25 18:06:49 -0700386 drivetrain.position.MakeWithBuilder()
387 .right_encoder(drivetrain_translate(right_encoder_->GetRaw()))
388 .left_encoder(-drivetrain_translate(left_encoder_->GetRaw()))
Brian Silverman20141f92015-01-05 17:39:01 -0800389 .left_shifter_position(0)
390 .right_shifter_position(0)
Austin Schuh010eb812014-10-25 18:06:49 -0700391 .battery_voltage(ds->GetBatteryVoltage())
392 .Send();
393
Brian Silvermand8f403a2014-12-13 19:12:04 -0500394 // Signal that we are alive.
Austin Schuh010eb812014-10-25 18:06:49 -0700395 ::aos::controls::sensor_generation.MakeWithBuilder()
396 .reader_pid(getpid())
397 .cape_resets(0)
398 .Send();
399 }
400
401 void Quit() { run_ = false; }
402
403 private:
404 ::std::unique_ptr<AnalogInput> auto_selector_analog_;
405
406 ::std::unique_ptr<Encoder> left_encoder_;
407 ::std::unique_ptr<Encoder> right_encoder_;
Austin Schuh010eb812014-10-25 18:06:49 -0700408
409 ::std::atomic<bool> run_;
410 DigitalGlitchFilter filter_;
411};
412
Brian Silvermand8f403a2014-12-13 19:12:04 -0500413class SolenoidWriter {
Austin Schuh010eb812014-10-25 18:06:49 -0700414 public:
Brian Silvermand8f403a2014-12-13 19:12:04 -0500415 SolenoidWriter(const ::std::unique_ptr<BufferedPcm> &pcm)
Brian Silverman20141f92015-01-05 17:39:01 -0800416 : pcm_(pcm), drivetrain_(".frc971.control_loops.drivetrain.output") {}
Brian Silvermand8f403a2014-12-13 19:12:04 -0500417
418 void set_drivetrain_left(::std::unique_ptr<BufferedSolenoid> s) {
419 drivetrain_left_ = ::std::move(s);
Austin Schuh010eb812014-10-25 18:06:49 -0700420 }
421
Brian Silvermand8f403a2014-12-13 19:12:04 -0500422 void set_drivetrain_right(::std::unique_ptr<BufferedSolenoid> s) {
423 drivetrain_right_ = ::std::move(s);
424 }
Austin Schuh010eb812014-10-25 18:06:49 -0700425
Brian Silvermand8f403a2014-12-13 19:12:04 -0500426 void operator()() {
427 ::aos::SetCurrentThreadName("Solenoids");
428 ::aos::SetCurrentThreadRealtimePriority(30);
429
430 while (run_) {
431 ::aos::time::PhasedLoopXMS(20, 1000);
432
433 {
434 drivetrain_.FetchLatest();
435 if (drivetrain_.get()) {
436 LOG_STRUCT(DEBUG, "solenoids", *drivetrain_);
437 drivetrain_left_->Set(drivetrain_->left_high);
438 drivetrain_right_->Set(drivetrain_->right_high);
439 }
440 }
441
Brian Silvermand8f403a2014-12-13 19:12:04 -0500442 pcm_->Flush();
Austin Schuh010eb812014-10-25 18:06:49 -0700443 }
444 }
445
Brian Silvermand8f403a2014-12-13 19:12:04 -0500446 void Quit() { run_ = false; }
Austin Schuh010eb812014-10-25 18:06:49 -0700447
Brian Silvermand8f403a2014-12-13 19:12:04 -0500448 private:
449 const ::std::unique_ptr<BufferedPcm> &pcm_;
450 ::std::unique_ptr<BufferedSolenoid> drivetrain_left_;
451 ::std::unique_ptr<BufferedSolenoid> drivetrain_right_;
Austin Schuh010eb812014-10-25 18:06:49 -0700452
Brian Silvermand8f403a2014-12-13 19:12:04 -0500453 ::aos::Queue<::frc971::control_loops::Drivetrain::Output> drivetrain_;
Austin Schuh010eb812014-10-25 18:06:49 -0700454
Brian Silvermand8f403a2014-12-13 19:12:04 -0500455 ::std::atomic<bool> run_{true};
456};
457
458class DrivetrainWriter : public LoopOutputHandler {
459 public:
460 void set_left_drivetrain_talon(::std::unique_ptr<Talon> t) {
461 left_drivetrain_talon_ = ::std::move(t);
Austin Schuh010eb812014-10-25 18:06:49 -0700462 }
463
Brian Silvermand8f403a2014-12-13 19:12:04 -0500464 void set_right_drivetrain_talon(::std::unique_ptr<Talon> t) {
465 right_drivetrain_talon_ = ::std::move(t);
466 }
Austin Schuh010eb812014-10-25 18:06:49 -0700467
Brian Silvermand8f403a2014-12-13 19:12:04 -0500468 private:
469 virtual void Read() override {
470 ::frc971::control_loops::drivetrain.output.FetchAnother();
471 }
472
473 virtual void Write() override {
474 auto &queue = ::frc971::control_loops::drivetrain.output;
475 LOG_STRUCT(DEBUG, "will output", *queue);
476 left_drivetrain_talon_->Set(-queue->left_voltage / 12.0);
477 right_drivetrain_talon_->Set(queue->right_voltage / 12.0);
478 }
479
480 virtual void Stop() override {
481 LOG(WARNING, "drivetrain output too old\n");
482 left_drivetrain_talon_->Disable();
483 right_drivetrain_talon_->Disable();
484 }
485
Austin Schuh010eb812014-10-25 18:06:49 -0700486 ::std::unique_ptr<Talon> left_drivetrain_talon_;
Brian Silvermand8f403a2014-12-13 19:12:04 -0500487 ::std::unique_ptr<Talon> right_drivetrain_talon_;
488};
489
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800490} // namespace wpilib
Austin Schuh010eb812014-10-25 18:06:49 -0700491} // namespace frc971
492
493class WPILibRobot : public RobotBase {
494 public:
495 virtual void StartCompetition() {
Brian Silvermand8f403a2014-12-13 19:12:04 -0500496 ::aos::InitNRT();
Brian Silverman2fe007c2014-12-28 12:20:01 -0800497 ::aos::SetCurrentThreadName("StartCompetition");
Brian Silvermand8f403a2014-12-13 19:12:04 -0500498
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800499 ::frc971::wpilib::JoystickSender joystick_sender;
Austin Schuh010eb812014-10-25 18:06:49 -0700500 ::std::thread joystick_thread(::std::ref(joystick_sender));
Brian Silvermand8f403a2014-12-13 19:12:04 -0500501 ::frc971::wpilib::SensorReader reader;
502 ::std::thread reader_thread(::std::ref(reader));
Brian Silverman07ec88e2014-12-28 00:13:08 -0800503 ::frc971::wpilib::GyroSender gyro_sender;
504 ::std::thread gyro_thread(::std::ref(gyro_sender));
Brian Silvermand8f403a2014-12-13 19:12:04 -0500505 ::std::unique_ptr<Compressor> compressor(new Compressor());
506 compressor->SetClosedLoopControl(true);
507
508 ::frc971::wpilib::DrivetrainWriter drivetrain_writer;
509 drivetrain_writer.set_left_drivetrain_talon(
510 ::std::unique_ptr<Talon>(new Talon(5)));
511 drivetrain_writer.set_right_drivetrain_talon(
512 ::std::unique_ptr<Talon>(new Talon(2)));
513 ::std::thread drivetrain_writer_thread(::std::ref(drivetrain_writer));
514
Brian Silvermand8f403a2014-12-13 19:12:04 -0500515 ::std::unique_ptr<::frc971::wpilib::BufferedPcm> pcm(
516 new ::frc971::wpilib::BufferedPcm());
517 ::frc971::wpilib::SolenoidWriter solenoid_writer(pcm);
518 solenoid_writer.set_drivetrain_left(pcm->MakeSolenoid(6));
519 solenoid_writer.set_drivetrain_right(pcm->MakeSolenoid(7));
Brian Silvermand8f403a2014-12-13 19:12:04 -0500520 ::std::thread solenoid_thread(::std::ref(solenoid_writer));
521
522 // Wait forever. Not much else to do...
523 PCHECK(select(0, nullptr, nullptr, nullptr, nullptr));
524
Austin Schuh010eb812014-10-25 18:06:49 -0700525 LOG(ERROR, "Exiting WPILibRobot\n");
Brian Silverman07ec88e2014-12-28 00:13:08 -0800526
Austin Schuh010eb812014-10-25 18:06:49 -0700527 joystick_sender.Quit();
528 joystick_thread.join();
Brian Silvermand8f403a2014-12-13 19:12:04 -0500529 reader.Quit();
530 reader_thread.join();
Brian Silverman07ec88e2014-12-28 00:13:08 -0800531 gyro_sender.Quit();
532 gyro_thread.join();
Brian Silvermand8f403a2014-12-13 19:12:04 -0500533
534 drivetrain_writer.Quit();
535 drivetrain_writer_thread.join();
Brian Silvermand8f403a2014-12-13 19:12:04 -0500536 solenoid_writer.Quit();
537 solenoid_thread.join();
538
Austin Schuh010eb812014-10-25 18:06:49 -0700539 ::aos::Cleanup();
540 }
541};
542
Austin Schuhdb516032014-12-28 00:12:38 -0800543
Austin Schuh010eb812014-10-25 18:06:49 -0700544START_ROBOT_CLASS(WPILibRobot);